mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-11-28 12:27:16 +03:00
chore(merge) feature/event-system (#12)
* feat(event-system) prototype * feat(event-system) prepare two way communication * feat(event-system) fASLR * feat(event-system) answer salt * feat(event-system) simplify communication and enable multi-level two way message passing
This commit is contained in:
parent
fcd0c7fc35
commit
b5927e4711
@ -27,9 +27,10 @@ flate2 = "1"
|
|||||||
hyper-old-types = "0.11.0"
|
hyper-old-types = "0.11.0"
|
||||||
sysinfo = "0.9"
|
sysinfo = "0.9"
|
||||||
webbrowser = "0.5.1"
|
webbrowser = "0.5.1"
|
||||||
|
uuid = { version = "0.7", features = ["v4"] }
|
||||||
|
lazy_static = "1.3.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
api = []
|
|
||||||
all-api = []
|
all-api = []
|
||||||
readTextFile = []
|
readTextFile = []
|
||||||
readBinaryFile = []
|
readBinaryFile = []
|
||||||
@ -39,3 +40,4 @@ listDirs = []
|
|||||||
setTitle = []
|
setTitle = []
|
||||||
execute = []
|
execute = []
|
||||||
open = []
|
open = []
|
||||||
|
answer = []
|
||||||
|
@ -4,78 +4,115 @@ use proton_ui::WebView;
|
|||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub fn handler<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> bool {
|
pub fn handler<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> bool {
|
||||||
#[cfg(feature = "api")]
|
use cmd::Cmd::*;
|
||||||
{
|
match serde_json::from_str(arg) {
|
||||||
use cmd::Cmd::*;
|
Err(_) => false,
|
||||||
match serde_json::from_str(arg) {
|
Ok(command) => {
|
||||||
Err(_) => false,
|
match command {
|
||||||
Ok(command) => {
|
#[cfg(any(feature = "all-api", feature = "readTextFile"))]
|
||||||
match command {
|
ReadTextFile {
|
||||||
#[cfg(any(feature = "all-api", feature = "readTextFile"))]
|
path,
|
||||||
ReadTextFile {
|
callback,
|
||||||
path,
|
error,
|
||||||
callback,
|
} => {
|
||||||
error,
|
super::file_system::read_text_file(webview, path, callback, error);
|
||||||
} => {
|
}
|
||||||
super::file_system::read_text_file(webview, path, callback, error);
|
#[cfg(any(feature = "all-api", feature = "readBinaryFile"))]
|
||||||
}
|
ReadBinaryFile {
|
||||||
#[cfg(any(feature = "all-api", feature = "readBinaryFile"))]
|
path,
|
||||||
ReadBinaryFile {
|
callback,
|
||||||
path,
|
error,
|
||||||
callback,
|
} => {
|
||||||
error,
|
super::file_system::read_binary_file(webview, path, callback, error);
|
||||||
} => {
|
}
|
||||||
super::file_system::read_binary_file(webview, path, callback, error);
|
#[cfg(any(feature = "all-api", feature = "writeFile"))]
|
||||||
}
|
WriteFile {
|
||||||
#[cfg(any(feature = "all-api", feature = "writeFile"))]
|
file,
|
||||||
WriteFile {
|
contents,
|
||||||
file,
|
callback,
|
||||||
contents,
|
error,
|
||||||
callback,
|
} => {
|
||||||
error,
|
super::file_system::write_file(webview, file, contents, callback, error);
|
||||||
} => {
|
}
|
||||||
super::file_system::write_file(webview, file, contents, callback, error);
|
#[cfg(any(feature = "all-api", feature = "listDirs"))]
|
||||||
}
|
ListDirs {
|
||||||
#[cfg(any(feature = "all-api", feature = "listDirs"))]
|
path,
|
||||||
ListDirs {
|
callback,
|
||||||
path,
|
error,
|
||||||
callback,
|
} => {
|
||||||
error,
|
super::file_system::list_dirs(webview, path, callback, error);
|
||||||
} => {
|
}
|
||||||
super::file_system::list_dirs(webview, path, callback, error);
|
#[cfg(any(feature = "all-api", feature = "listFiles"))]
|
||||||
}
|
ListFiles {
|
||||||
#[cfg(any(feature = "all-api", feature = "listFiles"))]
|
path,
|
||||||
ListFiles {
|
callback,
|
||||||
path,
|
error,
|
||||||
callback,
|
} => {
|
||||||
error,
|
super::file_system::list(webview, path, callback, error);
|
||||||
} => {
|
}
|
||||||
super::file_system::list(webview, path, callback, error);
|
#[cfg(any(feature = "all-api", feature = "setTitle"))]
|
||||||
}
|
SetTitle { title } => {
|
||||||
#[cfg(any(feature = "all-api", feature = "setTitle"))]
|
webview.set_title(&title).unwrap();
|
||||||
SetTitle { title } => {
|
}
|
||||||
webview.set_title(&title).unwrap();
|
#[cfg(any(feature = "all-api", feature = "execute"))]
|
||||||
}
|
Execute {
|
||||||
#[cfg(any(feature = "all-api", feature = "execute"))]
|
command,
|
||||||
Execute {
|
args,
|
||||||
command,
|
callback,
|
||||||
args,
|
error,
|
||||||
callback,
|
} => {
|
||||||
error,
|
super::command::call(webview, command, args, callback, error);
|
||||||
} => {
|
}
|
||||||
super::command::call(webview, command, args, callback, error);
|
#[cfg(any(feature = "all-api", feature = "open"))]
|
||||||
},
|
Open { uri } => {
|
||||||
#[cfg(any(feature = "all-api", feature = "open"))]
|
super::spawn(move || {
|
||||||
Open { uri } => {
|
webbrowser::open(&uri).unwrap();
|
||||||
super::spawn(move || {
|
});
|
||||||
webbrowser::open(&uri).unwrap();
|
}
|
||||||
});
|
|
||||||
}
|
ValidateSalt {
|
||||||
|
salt,
|
||||||
|
callback,
|
||||||
|
error,
|
||||||
|
} => {
|
||||||
|
crate::salt::validate(webview, salt, callback, error);
|
||||||
|
}
|
||||||
|
AddEventListener {
|
||||||
|
event,
|
||||||
|
handler,
|
||||||
|
once,
|
||||||
|
} => {
|
||||||
|
webview
|
||||||
|
.eval(&format!(
|
||||||
|
"
|
||||||
|
if (window['{obj}'] === void 0) {{
|
||||||
|
window['{obj}'] = {{}}
|
||||||
|
}}
|
||||||
|
if (window['{obj}']['{evt}'] === void 0) {{
|
||||||
|
window['{obj}']['{evt}'] = []
|
||||||
|
}}
|
||||||
|
window['{obj}']['{evt}'].push({{
|
||||||
|
handler: window['{handler}'],
|
||||||
|
once: {once_flag}
|
||||||
|
}})
|
||||||
|
",
|
||||||
|
obj = crate::event::event_listeners_object_name(),
|
||||||
|
evt = event,
|
||||||
|
handler = handler,
|
||||||
|
once_flag = if once { "true" } else { "false" }
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
#[cfg(any(feature = "all-api", feature = "answer"))]
|
||||||
|
Answer {
|
||||||
|
event_id,
|
||||||
|
payload,
|
||||||
|
salt,
|
||||||
|
} => {
|
||||||
|
crate::event::answer(event_id, payload, salt);
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "api"))]
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(tag = "cmd", rename_all = "camelCase")]
|
#[serde(tag = "cmd", rename_all = "camelCase")]
|
||||||
#[cfg(feature = "api")]
|
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
#[cfg(any(feature = "all-api", feature = "readTextFile"))]
|
#[cfg(any(feature = "all-api", feature = "readTextFile"))]
|
||||||
ReadTextFile {
|
ReadTextFile {
|
||||||
@ -43,5 +42,21 @@ pub enum Cmd {
|
|||||||
error: String,
|
error: String,
|
||||||
},
|
},
|
||||||
#[cfg(any(feature = "all-api", feature = "open"))]
|
#[cfg(any(feature = "all-api", feature = "open"))]
|
||||||
Open { uri: String }
|
Open { uri: String },
|
||||||
|
ValidateSalt {
|
||||||
|
salt: String,
|
||||||
|
callback: String,
|
||||||
|
error: String,
|
||||||
|
},
|
||||||
|
AddEventListener {
|
||||||
|
event: String,
|
||||||
|
handler: String,
|
||||||
|
once: bool,
|
||||||
|
},
|
||||||
|
#[cfg(any(feature = "all-api", feature = "answer"))]
|
||||||
|
Answer {
|
||||||
|
event_id: String,
|
||||||
|
payload: String,
|
||||||
|
salt: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use proton_ui::WebView;
|
|||||||
|
|
||||||
use std::process::{Child, Command, Stdio};
|
use std::process::{Child, Command, Stdio};
|
||||||
|
|
||||||
use super::run_async;
|
use crate::execute_promise;
|
||||||
|
|
||||||
pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> Result<String, String> {
|
pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> Result<String, String> {
|
||||||
Command::new(cmd)
|
Command::new(cmd)
|
||||||
@ -65,7 +65,7 @@ pub fn call<T: 'static>(
|
|||||||
callback: String,
|
callback: String,
|
||||||
error: String,
|
error: String,
|
||||||
) {
|
) {
|
||||||
run_async(
|
execute_promise(
|
||||||
webview,
|
webview,
|
||||||
|| {
|
|| {
|
||||||
get_output(command, args, Stdio::piped())
|
get_output(command, args, Stdio::piped())
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
use tempfile;
|
use tempfile;
|
||||||
|
|
||||||
mod utils;
|
mod utils;
|
||||||
|
78
lib/rust/src/event.rs
Normal file
78
lib/rust/src/event.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use proton_ui::{Handle, WebView};
|
||||||
|
use std::boxed::Box;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
struct EventHandler {
|
||||||
|
on_event: Box<dyn FnOnce(String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local!(static LISTENERS: Arc<Mutex<HashMap<String, EventHandler>>> = Arc::new(Mutex::new(HashMap::new())));
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref PROMPT_FUNCTION_NAME: String = uuid::Uuid::new_v4().to_string();
|
||||||
|
static ref EVENT_LISTENERS_OBJECT_NAME: String = uuid::Uuid::new_v4().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prompt_function_name() -> String {
|
||||||
|
PROMPT_FUNCTION_NAME.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event_listeners_object_name() -> String {
|
||||||
|
EVENT_LISTENERS_OBJECT_NAME.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prompt<T: 'static, F: FnOnce(String) + 'static>(
|
||||||
|
webview: &mut WebView<'_, T>,
|
||||||
|
id: &'static str,
|
||||||
|
payload: String,
|
||||||
|
handler: F,
|
||||||
|
) {
|
||||||
|
LISTENERS.with(|listeners| {
|
||||||
|
let mut l = listeners.lock().unwrap();
|
||||||
|
l.insert(
|
||||||
|
id.to_string(),
|
||||||
|
EventHandler {
|
||||||
|
on_event: Box::new(handler),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
trigger(webview.handle(), id, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger<T: 'static>(webview_handle: Handle<T>, id: &'static str, mut payload: String) {
|
||||||
|
let salt = crate::salt::generate();
|
||||||
|
if payload == "" {
|
||||||
|
payload = "void 0".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
webview_handle
|
||||||
|
.dispatch(move |_webview| {
|
||||||
|
_webview.eval(&format!(
|
||||||
|
"window['{}']({{type: '{}', payload: {}}}, '{}')",
|
||||||
|
prompt_function_name(),
|
||||||
|
id,
|
||||||
|
payload,
|
||||||
|
salt
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn answer(id: String, data: String, salt: String) {
|
||||||
|
if crate::salt::is_valid(salt) {
|
||||||
|
LISTENERS.with(|l| {
|
||||||
|
let mut listeners = l.lock().unwrap();
|
||||||
|
|
||||||
|
let key = id.clone();
|
||||||
|
|
||||||
|
if listeners.contains_key(&id) {
|
||||||
|
let handler = listeners.remove(&id).unwrap();
|
||||||
|
(handler.on_event)(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners.remove(&key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
use proton_ui::WebView;
|
use proton_ui::WebView;
|
||||||
|
|
||||||
use crate::dir;
|
use crate::dir;
|
||||||
|
use crate::execute_promise;
|
||||||
use crate::file;
|
use crate::file;
|
||||||
use crate::run_async;
|
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@ -13,7 +13,7 @@ pub fn list<T: 'static>(
|
|||||||
callback: String,
|
callback: String,
|
||||||
error: String,
|
error: String,
|
||||||
) {
|
) {
|
||||||
run_async(
|
execute_promise(
|
||||||
webview,
|
webview,
|
||||||
move || {
|
move || {
|
||||||
dir::walk_dir(path.to_string())
|
dir::walk_dir(path.to_string())
|
||||||
@ -30,7 +30,7 @@ pub fn list_dirs<T: 'static>(
|
|||||||
callback: String,
|
callback: String,
|
||||||
error: String,
|
error: String,
|
||||||
) {
|
) {
|
||||||
run_async(
|
execute_promise(
|
||||||
webview,
|
webview,
|
||||||
move || {
|
move || {
|
||||||
dir::list_dir_contents(&path)
|
dir::list_dir_contents(&path)
|
||||||
@ -48,7 +48,7 @@ pub fn write_file<T: 'static>(
|
|||||||
callback: String,
|
callback: String,
|
||||||
error: String,
|
error: String,
|
||||||
) {
|
) {
|
||||||
run_async(
|
execute_promise(
|
||||||
webview,
|
webview,
|
||||||
move || {
|
move || {
|
||||||
File::create(file)
|
File::create(file)
|
||||||
@ -70,7 +70,7 @@ pub fn read_text_file<T: 'static>(
|
|||||||
callback: String,
|
callback: String,
|
||||||
error: String,
|
error: String,
|
||||||
) {
|
) {
|
||||||
run_async(
|
execute_promise(
|
||||||
webview,
|
webview,
|
||||||
move || {
|
move || {
|
||||||
file::read_string(path).and_then(|f| {
|
file::read_string(path).and_then(|f| {
|
||||||
@ -90,7 +90,7 @@ pub fn read_binary_file<T: 'static>(
|
|||||||
callback: String,
|
callback: String,
|
||||||
error: String,
|
error: String,
|
||||||
) {
|
) {
|
||||||
run_async(
|
execute_promise(
|
||||||
webview,
|
webview,
|
||||||
move || {
|
move || {
|
||||||
file::read_binary(path).and_then(|f| {
|
file::read_binary(path).and_then(|f| {
|
||||||
|
@ -4,15 +4,20 @@ extern crate serde_derive;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod dir;
|
pub mod dir;
|
||||||
|
pub mod event;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod file_system;
|
pub mod file_system;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
|
pub mod salt;
|
||||||
pub mod tcp;
|
pub mod tcp;
|
||||||
pub mod updater;
|
pub mod updater;
|
||||||
pub mod version;
|
pub mod version;
|
||||||
@ -23,9 +28,7 @@ use threadpool::ThreadPool;
|
|||||||
|
|
||||||
thread_local!(static POOL: ThreadPool = ThreadPool::new(4));
|
thread_local!(static POOL: ThreadPool = ThreadPool::new(4));
|
||||||
|
|
||||||
pub fn spawn<F: FnOnce() -> () + Send + 'static>(
|
pub fn spawn<F: FnOnce() -> () + Send + 'static>(what: F) {
|
||||||
what: F
|
|
||||||
) {
|
|
||||||
POOL.with(|thread| {
|
POOL.with(|thread| {
|
||||||
thread.execute(move || {
|
thread.execute(move || {
|
||||||
what();
|
what();
|
||||||
@ -33,7 +36,13 @@ pub fn spawn<F: FnOnce() -> () + Send + 'static>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_async<T: 'static, F: FnOnce() -> Result<String, String> + Send + 'static>(
|
pub fn run_async<F: FnOnce() -> () + Send + 'static>(what: F) {
|
||||||
|
POOL.with(|thread| {
|
||||||
|
thread.execute(move || what());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_promise<T: 'static, F: FnOnce() -> Result<String, String> + Send + 'static>(
|
||||||
webview: &mut WebView<'_, T>,
|
webview: &mut WebView<'_, T>,
|
||||||
what: F,
|
what: F,
|
||||||
callback: String,
|
callback: String,
|
||||||
|
63
lib/rust/src/salt.rs
Normal file
63
lib/rust/src/salt.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use proton_ui::WebView;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
struct Salt {
|
||||||
|
value: String,
|
||||||
|
one_time: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref SALTS: Mutex<Vec<Salt>> = Mutex::new(vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate() -> String {
|
||||||
|
let salt = Uuid::new_v4();
|
||||||
|
SALTS.lock().unwrap().push(Salt {
|
||||||
|
value: salt.to_string(),
|
||||||
|
one_time: true,
|
||||||
|
});
|
||||||
|
return salt.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_static() -> String {
|
||||||
|
let salt = Uuid::new_v4();
|
||||||
|
SALTS.lock().unwrap().push(Salt {
|
||||||
|
value: salt.to_string(),
|
||||||
|
one_time: false,
|
||||||
|
});
|
||||||
|
return salt.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_valid(salt: String) -> bool {
|
||||||
|
let mut salts = SALTS.lock().unwrap();
|
||||||
|
match salts.iter().position(|s| s.value == salt) {
|
||||||
|
Some(index) => {
|
||||||
|
if salts[index].one_time {
|
||||||
|
salts.remove(index);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate<T: 'static>(
|
||||||
|
webview: &mut WebView<'_, T>,
|
||||||
|
salt: String,
|
||||||
|
callback: String,
|
||||||
|
error: String,
|
||||||
|
) {
|
||||||
|
crate::execute_promise(
|
||||||
|
webview,
|
||||||
|
move || {
|
||||||
|
if is_valid(salt) {
|
||||||
|
Ok("'VALID'".to_string())
|
||||||
|
} else {
|
||||||
|
Err("'INVALID SALT'".to_string())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
callback,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
@ -69,14 +69,14 @@ fn main() {
|
|||||||
debug = cfg!(debug_assertions);
|
debug = cfg!(debug_assertions);
|
||||||
#[cfg(feature = "serverless")]
|
#[cfg(feature = "serverless")]
|
||||||
{
|
{
|
||||||
fn inline_style(s: &str) -> String {
|
fn inline_style(s: &str) -> String {
|
||||||
format!(r#"<style type="text/css">{}</style>"#, s)
|
format!(r#"<style type="text/css">{}</style>"#, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inline_script(s: &str) -> String {
|
fn inline_script(s: &str) -> String {
|
||||||
format!(r#"<script type="text/javascript">{}</script>"#, s)
|
format!(r#"<script type="text/javascript">{}</script>"#, s)
|
||||||
}
|
}
|
||||||
let html = format!(r#"<!DOCTYPE html><html><head><meta http-equiv="Content-Security-Policy" content="default-src data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'">{styles}</head><body><div id="q-app"></div>{scripts}</body></html>"#,
|
let html = format!(r#"<!DOCTYPE html><html><head><meta http-equiv="Content-Security-Policy" content="default-src data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'">{styles}</head><body><div id="q-app"></div>{scripts}</body></html>"#,
|
||||||
styles = inline_style(include_str!("../target/compiled-web/css/app.css")),
|
styles = inline_style(include_str!("../target/compiled-web/css/app.css")),
|
||||||
scripts = inline_script(include_str!("../target/compiled-web/js/app.js")),
|
scripts = inline_script(include_str!("../target/compiled-web/js/app.js")),
|
||||||
);
|
);
|
||||||
@ -123,9 +123,46 @@ fn main() {
|
|||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
webview
|
||||||
|
.handle()
|
||||||
|
.dispatch(move |_webview| {
|
||||||
|
_webview
|
||||||
|
.eval(&format!(
|
||||||
|
"window['{fn}'] = (payload, salt) => {{
|
||||||
|
window.proton.promisified({{
|
||||||
|
cmd: 'validateSalt',
|
||||||
|
salt
|
||||||
|
}}).then(() => {{
|
||||||
|
const listeners = (window['{obj}'] && window['{obj}'][payload.type]) || []
|
||||||
|
for (let i = listeners.length - 1; i >= 0; i--) {{
|
||||||
|
const listener = listeners[i]
|
||||||
|
if (listener.once)
|
||||||
|
listeners.splice(i, 1)
|
||||||
|
const response = listener.handler(payload)
|
||||||
|
response && response
|
||||||
|
.then(result => {{
|
||||||
|
window.proton.invoke({{
|
||||||
|
cmd: 'answer',
|
||||||
|
event_id: payload.type,
|
||||||
|
payload: result,
|
||||||
|
salt: '{salt}'
|
||||||
|
}})
|
||||||
|
}})
|
||||||
|
}}
|
||||||
|
}})
|
||||||
|
}}",
|
||||||
|
fn = proton::event::prompt_function_name(),
|
||||||
|
obj = proton::event::event_listeners_object_name(),
|
||||||
|
salt = proton::salt::generate_static()
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
#[cfg(not(feature = "dev"))]
|
#[cfg(not(feature = "dev"))]
|
||||||
{
|
{
|
||||||
|
|
||||||
#[cfg(not(feature = "serverless"))]
|
#[cfg(not(feature = "serverless"))]
|
||||||
{
|
{
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
Loading…
Reference in New Issue
Block a user