mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-07-24 17:20:51 +02:00
feat: update to alpha.17, typed mobile plugin IPC arguments (#676)
Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
This commit is contained in:
committed by
GitHub
parent
76cfdc32b4
commit
e438e0a62d
@@ -2,7 +2,7 @@
|
||||
|
||||
## \[2.0.0-alpha.2]
|
||||
|
||||
- [`5c13736`](https://github.com/tauri-apps/plugins-workspace/commit/5c137365c60790e8d4037d449e8237aa3fffdab0)([#673](https://github.com/tauri-apps/plugins-workspace/pull/673)) Update to @tauri-apps/api v2.0.0-alpha.16.
|
||||
- [`5c13736`](https://github.com/tauri-apps/plugins-workspace/commit/5c137365c60790e8d4037d449e8237aa3fffdab0)([#673](https://github.com/tauri-apps/plugins-workspace/pull/673)) Update to @tauri-apps/api v2.0.0-alpha.9.
|
||||
|
||||
## \[2.0.0-alpha.2]
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ rust-version = { workspace = true }
|
||||
links = "tauri-plugin-dialog"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = [ "dox" ]
|
||||
rustc-args = [ "--cfg", "docsrs" ]
|
||||
rustdoc-args = [ "--cfg", "docsrs" ]
|
||||
targets = [ "x86_64-unknown-linux-gnu", "x86_64-linux-android" ]
|
||||
|
||||
[dependencies]
|
||||
@@ -24,11 +25,8 @@ tauri-plugin-fs = { path = "../fs", version = "2.0.0-alpha.3" }
|
||||
glib = "0.16"
|
||||
|
||||
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||
rfd = { version = "0.11", features = [ "gtk3", "common-controls-v6" ] }
|
||||
rfd = { version = "0.12", features = [ "gtk3", "common-controls-v6" ] }
|
||||
raw-window-handle = "0.5"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { workspace = true }
|
||||
|
||||
[features]
|
||||
dox = [ "tauri/dox" ]
|
||||
|
||||
@@ -14,24 +14,44 @@ import androidx.activity.result.ActivityResult
|
||||
import app.tauri.Logger
|
||||
import app.tauri.annotation.ActivityCallback
|
||||
import app.tauri.annotation.Command
|
||||
import app.tauri.annotation.InvokeArg
|
||||
import app.tauri.annotation.TauriPlugin
|
||||
import app.tauri.plugin.Invoke
|
||||
import app.tauri.plugin.JSArray
|
||||
import app.tauri.plugin.JSObject
|
||||
import app.tauri.plugin.Plugin
|
||||
import org.json.JSONException
|
||||
|
||||
@InvokeArg
|
||||
class Filter {
|
||||
lateinit var extensions: Array<String>
|
||||
}
|
||||
|
||||
@InvokeArg
|
||||
class FilePickerOptions {
|
||||
lateinit var filters: Array<Filter>
|
||||
var multiple: Boolean? = null
|
||||
var readData: Boolean? = null
|
||||
}
|
||||
|
||||
@InvokeArg
|
||||
class MessageOptions {
|
||||
var title: String?
|
||||
lateinit var message: String
|
||||
var okButtonLabel: String?
|
||||
var cancelButtonLabel: String?
|
||||
}
|
||||
|
||||
@TauriPlugin
|
||||
class DialogPlugin(private val activity: Activity): Plugin(activity) {
|
||||
var filePickerOptions: FilePickerOptions? = null
|
||||
|
||||
@Command
|
||||
fun showFilePicker(invoke: Invoke) {
|
||||
try {
|
||||
val filters = invoke.getArray("filters", JSArray())
|
||||
val multiple = invoke.getBoolean("multiple", false)
|
||||
val parsedTypes = parseFiltersOption(filters)
|
||||
val args = invoke.parseArgs(FilePickerOptions::class.java)
|
||||
val parsedTypes = parseFiltersOption(args.filters)
|
||||
|
||||
val intent = if (parsedTypes != null && parsedTypes.isNotEmpty()) {
|
||||
val intent = if (parsedTypes.isNotEmpty()) {
|
||||
val intent = Intent(Intent.ACTION_PICK)
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, parsedTypes)
|
||||
|
||||
@@ -55,7 +75,7 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
|
||||
intent
|
||||
}
|
||||
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple)
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, args.multiple ?: false)
|
||||
|
||||
startActivityForResult(invoke, intent, "filePickerResult")
|
||||
} catch (ex: Exception) {
|
||||
@@ -68,10 +88,9 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
|
||||
@ActivityCallback
|
||||
fun filePickerResult(invoke: Invoke, result: ActivityResult) {
|
||||
try {
|
||||
val readData = invoke.getBoolean("readData", false)
|
||||
when (result.resultCode) {
|
||||
Activity.RESULT_OK -> {
|
||||
val callResult = createPickFilesResult(result.data, readData)
|
||||
val callResult = createPickFilesResult(result.data, filePickerOptions?.readData ?: false)
|
||||
invoke.resolve(callResult)
|
||||
}
|
||||
Activity.RESULT_CANCELED -> invoke.reject("File picker cancelled")
|
||||
@@ -130,36 +149,19 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
|
||||
return callResult
|
||||
}
|
||||
|
||||
private fun parseFiltersOption(filters: JSArray): Array<String>? {
|
||||
return try {
|
||||
val filtersList: List<JSObject> = filters.toList()
|
||||
val mimeTypes = mutableListOf<String>()
|
||||
for (filter in filtersList) {
|
||||
val extensionsList = filter.getJSONArray("extensions")
|
||||
for (i in 0 until extensionsList.length()) {
|
||||
val mime = extensionsList.getString(i)
|
||||
mimeTypes.add(if (mime == "text/csv") "text/comma-separated-values" else mime)
|
||||
}
|
||||
private fun parseFiltersOption(filters: Array<Filter>): Array<String> {
|
||||
val mimeTypes = mutableListOf<String>()
|
||||
for (filter in filters) {
|
||||
for (mime in filter.extensions) {
|
||||
mimeTypes.add(if (mime == "text/csv") "text/comma-separated-values" else mime)
|
||||
}
|
||||
|
||||
mimeTypes.toTypedArray()
|
||||
} catch (exception: JSONException) {
|
||||
Logger.error("parseTypesOption failed.", exception)
|
||||
null
|
||||
}
|
||||
return mimeTypes.toTypedArray()
|
||||
}
|
||||
|
||||
@Command
|
||||
fun showMessageDialog(invoke: Invoke) {
|
||||
val title = invoke.getString("title")
|
||||
val message = invoke.getString("message")
|
||||
val okButtonLabel = invoke.getString("okButtonLabel", "OK")
|
||||
val cancelButtonLabel = invoke.getString("cancelButtonLabel", "Cancel")
|
||||
|
||||
if (message == null) {
|
||||
invoke.reject("The `message` argument is required")
|
||||
return
|
||||
}
|
||||
val args = invoke.parseArgs(MessageOptions::class.java)
|
||||
|
||||
if (activity.isFinishing) {
|
||||
invoke.reject("App is finishing")
|
||||
@@ -177,19 +179,19 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
|
||||
.post {
|
||||
val builder = AlertDialog.Builder(activity)
|
||||
|
||||
if (title != null) {
|
||||
builder.setTitle(title)
|
||||
if (args.title != null) {
|
||||
builder.setTitle(args.title)
|
||||
}
|
||||
builder
|
||||
.setMessage(message)
|
||||
.setMessage(args.message)
|
||||
.setPositiveButton(
|
||||
okButtonLabel
|
||||
args.okButtonLabel ?: "OK"
|
||||
) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
handler(false, true)
|
||||
}
|
||||
.setNegativeButton(
|
||||
cancelButtonLabel
|
||||
args.cancelButtonLabel ?: "Cancel"
|
||||
) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
handler(false, false)
|
||||
|
||||
@@ -10,7 +10,7 @@ fn main() {
|
||||
{
|
||||
println!("{error:#}");
|
||||
// when building documentation for Android the plugin build result is irrelevant to the crate itself
|
||||
if !(cfg!(feature = "dox") && std::env::var("TARGET").unwrap().contains("android")) {
|
||||
if !(cfg!(docsrs) && std::env::var("TARGET").unwrap().contains("android")) {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,207 +2,223 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import UIKit
|
||||
import MobileCoreServices
|
||||
import PhotosUI
|
||||
import Photos
|
||||
import WebKit
|
||||
import Tauri
|
||||
import PhotosUI
|
||||
import SwiftRs
|
||||
import Tauri
|
||||
import UIKit
|
||||
import WebKit
|
||||
|
||||
enum FilePickerEvent {
|
||||
case selected([URL])
|
||||
case cancelled
|
||||
case error(String)
|
||||
case selected([URL])
|
||||
case cancelled
|
||||
case error(String)
|
||||
}
|
||||
|
||||
struct MessageDialogOptions: Decodable {
|
||||
let title: String?
|
||||
let message: String
|
||||
var okButtonLabel = "OK"
|
||||
var cancelButtonLabel = "Cancel"
|
||||
}
|
||||
|
||||
struct Filter: Decodable {
|
||||
var extensions: [String] = []
|
||||
}
|
||||
|
||||
struct FilePickerOptions: Decodable {
|
||||
var multiple = false
|
||||
var readData = false
|
||||
var filters: [Filter] = []
|
||||
}
|
||||
|
||||
class DialogPlugin: Plugin {
|
||||
|
||||
var filePickerController: FilePickerController!
|
||||
var pendingInvoke: Invoke? = nil
|
||||
var filePickerController: FilePickerController!
|
||||
var pendingInvoke: Invoke? = nil
|
||||
var pendingInvokeArgs: FilePickerOptions? = nil
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
filePickerController = FilePickerController(self)
|
||||
}
|
||||
|
||||
@objc public func showFilePicker(_ invoke: Invoke) {
|
||||
let multiple = invoke.getBool("multiple", false)
|
||||
let filters = invoke.getArray("filters") ?? []
|
||||
let parsedTypes = parseFiltersOption(filters)
|
||||
|
||||
var isMedia = true
|
||||
var uniqueMimeType: Bool? = nil
|
||||
var mimeKind: String? = nil
|
||||
if !parsedTypes.isEmpty {
|
||||
uniqueMimeType = true
|
||||
for mime in parsedTypes {
|
||||
let kind = mime.components(separatedBy: "/")[0]
|
||||
if kind != "image" && kind != "video" {
|
||||
isMedia = false
|
||||
}
|
||||
if (mimeKind == nil) {
|
||||
mimeKind = kind
|
||||
} else if (mimeKind != kind) {
|
||||
uniqueMimeType = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pendingInvoke = invoke
|
||||
|
||||
if uniqueMimeType == true || isMedia {
|
||||
DispatchQueue.main.async {
|
||||
if #available(iOS 14, *) {
|
||||
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
|
||||
configuration.selectionLimit = multiple ? 0 : 1
|
||||
|
||||
if uniqueMimeType == true {
|
||||
if mimeKind == "image" {
|
||||
configuration.filter = .images
|
||||
} else if mimeKind == "video" {
|
||||
configuration.filter = .videos
|
||||
}
|
||||
}
|
||||
|
||||
let picker = PHPickerViewController(configuration: configuration)
|
||||
picker.delegate = self.filePickerController
|
||||
picker.modalPresentationStyle = .fullScreen
|
||||
self.presentViewController(picker)
|
||||
} else {
|
||||
let picker = UIImagePickerController()
|
||||
picker.delegate = self.filePickerController
|
||||
|
||||
if uniqueMimeType == true && mimeKind == "image" {
|
||||
picker.sourceType = .photoLibrary
|
||||
}
|
||||
|
||||
picker.sourceType = .photoLibrary
|
||||
picker.modalPresentationStyle = .fullScreen
|
||||
self.presentViewController(picker)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let documentTypes = parsedTypes.isEmpty ? ["public.data"] : parsedTypes
|
||||
DispatchQueue.main.async {
|
||||
let picker = UIDocumentPickerViewController(documentTypes: documentTypes, in: .import)
|
||||
picker.delegate = self.filePickerController
|
||||
picker.allowsMultipleSelection = multiple
|
||||
picker.modalPresentationStyle = .fullScreen
|
||||
self.presentViewController(picker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func presentViewController(_ viewControllerToPresent: UIViewController) {
|
||||
self.manager.viewController?.present(viewControllerToPresent, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
private func parseFiltersOption(_ filters: JSArray) -> [String] {
|
||||
var parsedTypes: [String] = []
|
||||
for (_, filter) in filters.enumerated() {
|
||||
let filterObj = filter as? JSObject
|
||||
if let filterObj = filterObj {
|
||||
let extensions = filterObj["extensions"] as? JSArray
|
||||
if let extensions = extensions {
|
||||
for e in extensions {
|
||||
let ext = e as? String ?? ""
|
||||
guard let utType: String = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, ext as CFString, nil)?.takeRetainedValue() as String? else {
|
||||
continue
|
||||
}
|
||||
parsedTypes.append(utType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return parsedTypes
|
||||
override init() {
|
||||
super.init()
|
||||
filePickerController = FilePickerController(self)
|
||||
}
|
||||
|
||||
public func onFilePickerEvent(_ event: FilePickerEvent) {
|
||||
switch event {
|
||||
case .selected(let urls):
|
||||
let readData = pendingInvoke?.getBool("readData", false) ?? false
|
||||
do {
|
||||
let filesResult = try urls.map {(url: URL) -> JSObject in
|
||||
var file = JSObject()
|
||||
@objc public func showFilePicker(_ invoke: Invoke) throws {
|
||||
let args = try invoke.parseArgs(FilePickerOptions.self)
|
||||
|
||||
let mimeType = filePickerController.getMimeTypeFromUrl(url)
|
||||
let isVideo = mimeType.hasPrefix("video")
|
||||
let isImage = mimeType.hasPrefix("image")
|
||||
let parsedTypes = parseFiltersOption(args.filters)
|
||||
|
||||
if readData {
|
||||
file["data"] = try Data(contentsOf: url).base64EncodedString()
|
||||
}
|
||||
|
||||
if isVideo {
|
||||
file["duration"] = filePickerController.getVideoDuration(url)
|
||||
let (height, width) = filePickerController.getVideoDimensions(url)
|
||||
if let height = height {
|
||||
file["height"] = height
|
||||
}
|
||||
if let width = width {
|
||||
file["width"] = width
|
||||
}
|
||||
} else if isImage {
|
||||
let (height, width) = filePickerController.getImageDimensions(url)
|
||||
if let height = height {
|
||||
file["height"] = height
|
||||
}
|
||||
if let width = width {
|
||||
file["width"] = width
|
||||
}
|
||||
}
|
||||
|
||||
file["modifiedAt"] = filePickerController.getModifiedAtFromUrl(url)
|
||||
file["mimeType"] = mimeType
|
||||
file["name"] = url.lastPathComponent
|
||||
file["path"] = url.absoluteString
|
||||
file["size"] = try filePickerController.getSizeFromUrl(url)
|
||||
return file
|
||||
}
|
||||
pendingInvoke?.resolve(["files": filesResult])
|
||||
} catch let error as NSError {
|
||||
pendingInvoke?.reject(error.localizedDescription, nil, error)
|
||||
return
|
||||
var isMedia = true
|
||||
var uniqueMimeType: Bool? = nil
|
||||
var mimeKind: String? = nil
|
||||
if !parsedTypes.isEmpty {
|
||||
uniqueMimeType = true
|
||||
for mime in parsedTypes {
|
||||
let kind = mime.components(separatedBy: "/")[0]
|
||||
if kind != "image" && kind != "video" {
|
||||
isMedia = false
|
||||
}
|
||||
if mimeKind == nil {
|
||||
mimeKind = kind
|
||||
} else if mimeKind != kind {
|
||||
uniqueMimeType = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pendingInvoke?.resolve(["files": urls])
|
||||
case .cancelled:
|
||||
let files: JSArray = []
|
||||
pendingInvoke?.resolve(["files": files])
|
||||
case .error(let error):
|
||||
pendingInvoke?.reject(error)
|
||||
}
|
||||
}
|
||||
pendingInvoke = invoke
|
||||
pendingInvokeArgs = args
|
||||
|
||||
@objc public func showMessageDialog(_ invoke: Invoke) {
|
||||
let manager = self.manager
|
||||
let title = invoke.getString("title")
|
||||
guard let message = invoke.getString("message") else {
|
||||
invoke.reject("The `message` argument is required")
|
||||
return
|
||||
}
|
||||
let okButtonLabel = invoke.getString("okButtonLabel") ?? "OK"
|
||||
let cancelButtonLabel = invoke.getString("cancelButtonLabel") ?? "Cancel"
|
||||
|
||||
DispatchQueue.main.async { [] in
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
|
||||
alert.addAction(UIAlertAction(title: cancelButtonLabel, style: UIAlertAction.Style.default, handler: { (_) -> Void in
|
||||
invoke.resolve([
|
||||
"value": false,
|
||||
"cancelled": false
|
||||
])
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: okButtonLabel, style: UIAlertAction.Style.default, handler: { (_) -> Void in
|
||||
invoke.resolve([
|
||||
"value": true,
|
||||
"cancelled": false
|
||||
])
|
||||
}))
|
||||
if uniqueMimeType == true || isMedia {
|
||||
DispatchQueue.main.async {
|
||||
if #available(iOS 14, *) {
|
||||
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
|
||||
configuration.selectionLimit = args.multiple ? 0 : 1
|
||||
|
||||
manager.viewController?.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
if uniqueMimeType == true {
|
||||
if mimeKind == "image" {
|
||||
configuration.filter = .images
|
||||
} else if mimeKind == "video" {
|
||||
configuration.filter = .videos
|
||||
}
|
||||
}
|
||||
|
||||
let picker = PHPickerViewController(configuration: configuration)
|
||||
picker.delegate = self.filePickerController
|
||||
picker.modalPresentationStyle = .fullScreen
|
||||
self.presentViewController(picker)
|
||||
} else {
|
||||
let picker = UIImagePickerController()
|
||||
picker.delegate = self.filePickerController
|
||||
|
||||
if uniqueMimeType == true && mimeKind == "image" {
|
||||
picker.sourceType = .photoLibrary
|
||||
}
|
||||
|
||||
picker.sourceType = .photoLibrary
|
||||
picker.modalPresentationStyle = .fullScreen
|
||||
self.presentViewController(picker)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let documentTypes = parsedTypes.isEmpty ? ["public.data"] : parsedTypes
|
||||
DispatchQueue.main.async {
|
||||
let picker = UIDocumentPickerViewController(documentTypes: documentTypes, in: .import)
|
||||
picker.delegate = self.filePickerController
|
||||
picker.allowsMultipleSelection = args.multiple
|
||||
picker.modalPresentationStyle = .fullScreen
|
||||
self.presentViewController(picker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func presentViewController(_ viewControllerToPresent: UIViewController) {
|
||||
self.manager.viewController?.present(viewControllerToPresent, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
private func parseFiltersOption(_ filters: [Filter]) -> [String] {
|
||||
var parsedTypes: [String] = []
|
||||
for filter in filters {
|
||||
for ext in filter.extensions {
|
||||
guard
|
||||
let utType: String = UTTypeCreatePreferredIdentifierForTag(
|
||||
kUTTagClassMIMEType, ext as CFString, nil)?.takeRetainedValue() as String?
|
||||
else {
|
||||
continue
|
||||
}
|
||||
parsedTypes.append(utType)
|
||||
}
|
||||
}
|
||||
return parsedTypes
|
||||
}
|
||||
|
||||
public func onFilePickerEvent(_ event: FilePickerEvent) {
|
||||
switch event {
|
||||
case .selected(let urls):
|
||||
let readData = pendingInvokeArgs?.readData ?? false
|
||||
do {
|
||||
let filesResult = try urls.map { (url: URL) -> JSObject in
|
||||
var file = JSObject()
|
||||
|
||||
let mimeType = filePickerController.getMimeTypeFromUrl(url)
|
||||
let isVideo = mimeType.hasPrefix("video")
|
||||
let isImage = mimeType.hasPrefix("image")
|
||||
|
||||
if readData {
|
||||
file["data"] = try Data(contentsOf: url).base64EncodedString()
|
||||
}
|
||||
|
||||
if isVideo {
|
||||
file["duration"] = filePickerController.getVideoDuration(url)
|
||||
let (height, width) = filePickerController.getVideoDimensions(url)
|
||||
if let height = height {
|
||||
file["height"] = height
|
||||
}
|
||||
if let width = width {
|
||||
file["width"] = width
|
||||
}
|
||||
} else if isImage {
|
||||
let (height, width) = filePickerController.getImageDimensions(url)
|
||||
if let height = height {
|
||||
file["height"] = height
|
||||
}
|
||||
if let width = width {
|
||||
file["width"] = width
|
||||
}
|
||||
}
|
||||
|
||||
file["modifiedAt"] = filePickerController.getModifiedAtFromUrl(url)
|
||||
file["mimeType"] = mimeType
|
||||
file["name"] = url.lastPathComponent
|
||||
file["path"] = url.absoluteString
|
||||
file["size"] = try filePickerController.getSizeFromUrl(url)
|
||||
return file
|
||||
}
|
||||
pendingInvoke?.resolve(["files": filesResult])
|
||||
} catch let error as NSError {
|
||||
pendingInvoke?.reject(error.localizedDescription, error: error)
|
||||
return
|
||||
}
|
||||
|
||||
pendingInvoke?.resolve(["files": urls])
|
||||
case .cancelled:
|
||||
let files: JSArray = []
|
||||
pendingInvoke?.resolve(["files": files])
|
||||
case .error(let error):
|
||||
pendingInvoke?.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func showMessageDialog(_ invoke: Invoke) throws {
|
||||
let manager = self.manager
|
||||
let args = try invoke.parseArgs(MessageDialogOptions.self)
|
||||
|
||||
DispatchQueue.main.async { [] in
|
||||
let alert = UIAlertController(
|
||||
title: args.title, message: args.message, preferredStyle: UIAlertController.Style.alert)
|
||||
alert.addAction(
|
||||
UIAlertAction(
|
||||
title: args.cancelButtonLabel, style: UIAlertAction.Style.default,
|
||||
handler: { (_) -> Void in
|
||||
invoke.resolve([
|
||||
"value": false,
|
||||
"cancelled": false,
|
||||
])
|
||||
}))
|
||||
alert.addAction(
|
||||
UIAlertAction(
|
||||
title: args.okButtonLabel, style: UIAlertAction.Style.default,
|
||||
handler: { (_) -> Void in
|
||||
invoke.resolve([
|
||||
"value": true,
|
||||
"cancelled": false,
|
||||
])
|
||||
}))
|
||||
|
||||
manager.viewController?.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@_cdecl("init_plugin_dialog")
|
||||
|
||||
@@ -27,6 +27,6 @@
|
||||
"tslib": "^2.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "2.0.0-alpha.9"
|
||||
"@tauri-apps/api": "2.0.0-alpha.11"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
if("__TAURI__"in window){var __TAURI_DIALOG__=function(n){"use strict";var t=Object.defineProperty,e=(n,t,e)=>{if(!t.has(n))throw TypeError("Cannot "+e)},i=(n,t,i)=>(e(n,t,"read from private field"),i?i.call(n):t.get(n));function o(n,t=!1){return window.__TAURI_INTERNALS__.transformCallback(n,t)}((n,e)=>{for(var i in e)t(n,i,{get:e[i],enumerable:!0})})({},{Channel:()=>r,PluginListener:()=>a,addPluginListener:()=>s,convertFileSrc:()=>d,invoke:()=>u,transformCallback:()=>o});var l,r=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,((n,t,e)=>{if(t.has(n))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(n):t.set(n,e)})(this,l,(()=>{})),this.id=o((n=>{i(this,l).call(this,n)}))}set onmessage(n){var t,i,o,r;o=n,e(t=this,i=l,"write to private field"),r?r.call(t,o):i.set(t,o)}get onmessage(){return i(this,l)}toJSON(){return`__CHANNEL__:${this.id}`}};l=new WeakMap;var a=class{constructor(n,t,e){this.plugin=n,this.event=t,this.channelId=e}async unregister(){return u(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function s(n,t,e){let i=new r;return i.onmessage=e,u(`plugin:${n}|register_listener`,{event:t,handler:i}).then((()=>new a(n,t,i.id)))}async function u(n,t={},e){return window.__TAURI_INTERNALS__.invoke(n,t,e)}function d(n,t="asset"){return window.__TAURI_INTERNALS__.convertFileSrc(n,t)}return n.ask=async function(n,t){var e,i,o,l,r;const a="string"==typeof t?{title:t}:t;return u("plugin:dialog|ask",{message:n.toString(),title:null===(e=null==a?void 0:a.title)||void 0===e?void 0:e.toString(),type_:null==a?void 0:a.type,okButtonLabel:null!==(o=null===(i=null==a?void 0:a.okLabel)||void 0===i?void 0:i.toString())&&void 0!==o?o:"Yes",cancelButtonLabel:null!==(r=null===(l=null==a?void 0:a.cancelLabel)||void 0===l?void 0:l.toString())&&void 0!==r?r:"No"})},n.confirm=async function(n,t){var e,i,o,l,r;const a="string"==typeof t?{title:t}:t;return u("plugin:dialog|confirm",{message:n.toString(),title:null===(e=null==a?void 0:a.title)||void 0===e?void 0:e.toString(),type_:null==a?void 0:a.type,okButtonLabel:null!==(o=null===(i=null==a?void 0:a.okLabel)||void 0===i?void 0:i.toString())&&void 0!==o?o:"Ok",cancelButtonLabel:null!==(r=null===(l=null==a?void 0:a.cancelLabel)||void 0===l?void 0:l.toString())&&void 0!==r?r:"Cancel"})},n.message=async function(n,t){var e,i;const o="string"==typeof t?{title:t}:t;return u("plugin:dialog|message",{message:n.toString(),title:null===(e=null==o?void 0:o.title)||void 0===e?void 0:e.toString(),type_:null==o?void 0:o.type,okButtonLabel:null===(i=null==o?void 0:o.okLabel)||void 0===i?void 0:i.toString()})},n.open=async function(n={}){return"object"==typeof n&&Object.freeze(n),u("plugin:dialog|open",{options:n})},n.save=async function(n={}){return"object"==typeof n&&Object.freeze(n),u("plugin:dialog|save",{options:n})},n}({});Object.defineProperty(window.__TAURI__,"dialog",{value:__TAURI_DIALOG__})}
|
||||
if("__TAURI__"in window){var __TAURI_DIALOG__=function(n){"use strict";async function o(n,o={},t){return window.__TAURI_INTERNALS__.invoke(n,o,t)}return"function"==typeof SuppressedError&&SuppressedError,n.ask=async function(n,t){var i,l,e,u,r;const d="string"==typeof t?{title:t}:t;return o("plugin:dialog|ask",{message:n.toString(),title:null===(i=null==d?void 0:d.title)||void 0===i?void 0:i.toString(),type_:null==d?void 0:d.type,okButtonLabel:null!==(e=null===(l=null==d?void 0:d.okLabel)||void 0===l?void 0:l.toString())&&void 0!==e?e:"Yes",cancelButtonLabel:null!==(r=null===(u=null==d?void 0:d.cancelLabel)||void 0===u?void 0:u.toString())&&void 0!==r?r:"No"})},n.confirm=async function(n,t){var i,l,e,u,r;const d="string"==typeof t?{title:t}:t;return o("plugin:dialog|confirm",{message:n.toString(),title:null===(i=null==d?void 0:d.title)||void 0===i?void 0:i.toString(),type_:null==d?void 0:d.type,okButtonLabel:null!==(e=null===(l=null==d?void 0:d.okLabel)||void 0===l?void 0:l.toString())&&void 0!==e?e:"Ok",cancelButtonLabel:null!==(r=null===(u=null==d?void 0:d.cancelLabel)||void 0===u?void 0:u.toString())&&void 0!==r?r:"Cancel"})},n.message=async function(n,t){var i,l;const e="string"==typeof t?{title:t}:t;return o("plugin:dialog|message",{message:n.toString(),title:null===(i=null==e?void 0:e.title)||void 0===i?void 0:i.toString(),type_:null==e?void 0:e.type,okButtonLabel:null===(l=null==e?void 0:e.okLabel)||void 0===l?void 0:l.toString()})},n.open=async function(n={}){return"object"==typeof n&&Object.freeze(n),o("plugin:dialog|open",{options:n})},n.save=async function(n={}){return"object"==typeof n&&Object.freeze(n),o("plugin:dialog|save",{options:n})},n}({});Object.defineProperty(window.__TAURI__,"dialog",{value:__TAURI_DIALOG__})}
|
||||
|
||||
@@ -56,7 +56,10 @@ macro_rules! run_dialog {
|
||||
($e:expr, $h: ident) => {{
|
||||
std::thread::spawn(move || {
|
||||
let response = tauri::async_runtime::block_on($e);
|
||||
$h(response);
|
||||
$h(!matches!(
|
||||
response,
|
||||
rfd::MessageDialogResult::No | rfd::MessageDialogResult::Cancel
|
||||
));
|
||||
});
|
||||
}};
|
||||
}
|
||||
@@ -68,7 +71,10 @@ macro_rules! run_dialog {
|
||||
let context = glib::MainContext::default();
|
||||
context.invoke_with_priority(glib::PRIORITY_HIGH, move || {
|
||||
let response = $e;
|
||||
$h(response);
|
||||
$h(!matches!(
|
||||
response,
|
||||
rfd::MessageDialogResult::No | rfd::MessageDialogResult::Cancel
|
||||
));
|
||||
});
|
||||
});
|
||||
}};
|
||||
|
||||
@@ -1 +1 @@
|
||||
!function(){"use strict";var e=Object.defineProperty,n=(e,n,t)=>{if(!n.has(e))throw TypeError("Cannot "+t)},t=(e,t,r)=>(n(e,t,"read from private field"),r?r.call(e):t.get(e));function r(e,n=!1){return window.__TAURI_INTERNALS__.transformCallback(e,n)}((n,t)=>{for(var r in t)e(n,r,{get:t[r],enumerable:!0})})({},{Channel:()=>s,PluginListener:()=>a,addPluginListener:()=>o,convertFileSrc:()=>c,invoke:()=>l,transformCallback:()=>r});var i,s=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,((e,n,t)=>{if(n.has(e))throw TypeError("Cannot add the same private member more than once");n instanceof WeakSet?n.add(e):n.set(e,t)})(this,i,(()=>{})),this.id=r((e=>{t(this,i).call(this,e)}))}set onmessage(e){var t,r,s,a;s=e,n(t=this,r=i,"write to private field"),a?a.call(t,s):r.set(t,s)}get onmessage(){return t(this,i)}toJSON(){return`__CHANNEL__:${this.id}`}};i=new WeakMap;var a=class{constructor(e,n,t){this.plugin=e,this.event=n,this.channelId=t}async unregister(){return l(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function o(e,n,t){let r=new s;return r.onmessage=t,l(`plugin:${e}|register_listener`,{event:n,handler:r}).then((()=>new a(e,n,r.id)))}async function l(e,n={},t){return window.__TAURI_INTERNALS__.invoke(e,n,t)}function c(e,n="asset"){return window.__TAURI_INTERNALS__.convertFileSrc(e,n)}window.alert=function(e){l("plugin:dialog|message",{message:e.toString()})},window.confirm=function(e){return l("plugin:dialog|confirm",{message:e.toString()})}}();
|
||||
!function(){"use strict";async function n(n,i={},o){return window.__TAURI_INTERNALS__.invoke(n,i,o)}"function"==typeof SuppressedError&&SuppressedError,window.alert=function(i){n("plugin:dialog|message",{message:i.toString()})},window.confirm=function(i){return n("plugin:dialog|confirm",{message:i.toString()})}}();
|
||||
|
||||
Reference in New Issue
Block a user