implement the file engine for liveview
This commit is contained in:
1 parent
6d3d927d5c
commit
9cfb655478
5 files changed
+113
-16
No files matched your search
@@ -23,11 +23,12 @@ fn module_loader(root_name: &str) -> String {
|
|||||||
if (type === "file") {
|
if (type === "file") {
|
||||||
let target_id = find_real_id(target);
|
let target_id = find_real_id(target);
|
||||||
if (target_id !== null) {
|
if (target_id !== null) {
|
||||||
const event_name = "change";
|
const send = (event_name) => {
|
||||||
const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name });
|
const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name });
|
||||||
console.log(message);
|
window.ipc.postMessage(message);
|
||||||
console.log(event);
|
};
|
||||||
window.ipc.postMessage(message);
|
send("change");
|
||||||
|
send("input");
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,55 @@ pub struct FormData {
|
|||||||
|
|
||||||
pub values: HashMap<String, Vec<String>>,
|
pub values: HashMap<String, Vec<String>>,
|
||||||
|
|
||||||
#[cfg_attr(feature = "serialize", serde(skip))]
|
#[cfg_attr(
|
||||||
|
feature = "serialize",
|
||||||
|
serde(skip_serializing, deserialize_with = "deserialize_file_engine")
|
||||||
|
)]
|
||||||
pub files: Option<std::sync::Arc<dyn FileEngine>>,
|
pub files: Option<std::sync::Arc<dyn FileEngine>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
struct SerializedFileEngine {
|
||||||
|
files: HashMap<String, Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl FileEngine for SerializedFileEngine {
|
||||||
|
fn files(&self) -> Vec<String> {
|
||||||
|
self.files.keys().cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
|
||||||
|
self.files.get(file).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_file_to_string(&self, file: &str) -> Option<String> {
|
||||||
|
self.read_file(file)
|
||||||
|
.await
|
||||||
|
.map(|bytes| String::from_utf8_lossy(&bytes).to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
fn deserialize_file_engine<'de, D>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<Option<std::sync::Arc<dyn FileEngine>>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
let Ok(file_engine) =
|
||||||
|
SerializedFileEngine::deserialize(deserializer) else{
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let file_engine = std::sync::Arc::new(file_engine);
|
||||||
|
Ok(Some(file_engine))
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq for FormData {
|
impl PartialEq for FormData {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.value == other.value && self.values == other.values
|
self.value == other.value && self.values == other.values
|
||||||
|
|||||||
@@ -344,17 +344,19 @@ class Interpreter {
|
|||||||
this.RemoveEventListener(edit.id, edit.name);
|
this.RemoveEventListener(edit.id, edit.name);
|
||||||
break;
|
break;
|
||||||
case "NewEventListener":
|
case "NewEventListener":
|
||||||
let bubbles = event_bubbles(edit.name);
|
const bubbles = event_bubbles(edit.name);
|
||||||
|
|
||||||
this.NewEventListener(edit.name, edit.id, bubbles, handler);
|
this.NewEventListener(edit.name, edit.id, bubbles, (event) =>
|
||||||
|
handler(event, edit.name, bubbles)
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this handler is only provided on desktop implementations since this
|
// this handler is only provided on the desktop and liveview implementations since this
|
||||||
// method is not used by the web implementation
|
// method is not used by the web implementation
|
||||||
function handler(event) {
|
function handler(event, name, bubbles) {
|
||||||
let target = event.target;
|
let target = event.target;
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
let shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`);
|
let shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`);
|
||||||
@@ -387,8 +389,6 @@ function handler(event) {
|
|||||||
|
|
||||||
shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`);
|
shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`);
|
||||||
|
|
||||||
let contents = serialize_event(event);
|
|
||||||
|
|
||||||
if (shouldPreventDefault === `on${event.type}`) {
|
if (shouldPreventDefault === `on${event.type}`) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
@@ -397,6 +397,10 @@ function handler(event) {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let contents = serialize_event(event);
|
||||||
|
|
||||||
|
/*POST_EVENT_SERIALIZATION*/
|
||||||
|
|
||||||
if (
|
if (
|
||||||
target.tagName === "FORM" &&
|
target.tagName === "FORM" &&
|
||||||
(event.type === "submit" || event.type === "input")
|
(event.type === "submit" || event.type === "input")
|
||||||
@@ -408,7 +412,8 @@ function handler(event) {
|
|||||||
const formData = new FormData(target);
|
const formData = new FormData(target);
|
||||||
|
|
||||||
for (let name of formData.keys()) {
|
for (let name of formData.keys()) {
|
||||||
contents.values[name] = formData.getAll(name);
|
let value = formData.getAll(name);
|
||||||
|
contents.values[name] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -418,7 +423,7 @@ function handler(event) {
|
|||||||
}
|
}
|
||||||
window.ipc.postMessage(
|
window.ipc.postMessage(
|
||||||
serializeIpcMessage("user_event", {
|
serializeIpcMessage("user_event", {
|
||||||
name: edit.name,
|
name: name,
|
||||||
element: parseInt(realId),
|
element: parseInt(realId),
|
||||||
data: contents,
|
data: contents,
|
||||||
bubbles,
|
bubbles,
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ axum = { version = "0.6.1", optional = true, features = ["ws"] }
|
|||||||
|
|
||||||
# salvo
|
# salvo
|
||||||
salvo = { version = "0.37.7", optional = true, features = ["ws"] }
|
salvo = { version = "0.37.7", optional = true, features = ["ws"] }
|
||||||
|
once_cell = "1.17.1"
|
||||||
|
|
||||||
# actix is ... complicated?
|
# actix is ... complicated?
|
||||||
# actix-files = { version = "0.6.2", optional = true }
|
# actix-files = { version = "0.6.2", optional = true }
|
||||||
|
|||||||
@@ -34,7 +34,51 @@ pub enum LiveViewError {
|
|||||||
SendingFailed,
|
SendingFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
use dioxus_interpreter_js::INTERPRETER_JS;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
static INTERPRETER_JS: Lazy<String> = Lazy::new(|| {
|
||||||
|
let interpreter = dioxus_interpreter_js::INTERPRETER_JS;
|
||||||
|
let serialize_file_uploads = r#"if (
|
||||||
|
target.tagName === "INPUT" &&
|
||||||
|
(event.type === "change" || event.type === "input")
|
||||||
|
) {
|
||||||
|
const type = target.getAttribute("type");
|
||||||
|
if (type === "file") {
|
||||||
|
async function read_files() {
|
||||||
|
const files = target.files;
|
||||||
|
const file_contents = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const file = files[i];
|
||||||
|
|
||||||
|
file_contents[file.name] = Array.from(
|
||||||
|
new Uint8Array(await file.arrayBuffer())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let file_engine = {
|
||||||
|
files: file_contents,
|
||||||
|
};
|
||||||
|
contents.files = file_engine;
|
||||||
|
|
||||||
|
if (realId === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const message = serializeIpcMessage("user_event", {
|
||||||
|
name: name,
|
||||||
|
element: parseInt(realId),
|
||||||
|
data: contents,
|
||||||
|
bubbles,
|
||||||
|
});
|
||||||
|
window.ipc.postMessage(message);
|
||||||
|
}
|
||||||
|
read_files();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
interpreter.replace("/*POST_EVENT_SERIALIZATION*/", serialize_file_uploads)
|
||||||
|
});
|
||||||
|
|
||||||
static MAIN_JS: &str = include_str!("./main.js");
|
static MAIN_JS: &str = include_str!("./main.js");
|
||||||
|
|
||||||
/// This script that gets injected into your app connects this page to the websocket endpoint
|
/// This script that gets injected into your app connects this page to the websocket endpoint
|
||||||
@@ -42,11 +86,12 @@ static MAIN_JS: &str = include_str!("./main.js");
|
|||||||
/// Once the endpoint is connected, it will send the initial state of the app, and then start
|
/// Once the endpoint is connected, it will send the initial state of the app, and then start
|
||||||
/// processing user events and returning edits to the liveview instance
|
/// processing user events and returning edits to the liveview instance
|
||||||
pub fn interpreter_glue(url: &str) -> String {
|
pub fn interpreter_glue(url: &str) -> String {
|
||||||
|
let js = &*INTERPRETER_JS;
|
||||||
format!(
|
format!(
|
||||||
r#"
|
r#"
|
||||||
<script>
|
<script>
|
||||||
var WS_ADDR = "{url}";
|
var WS_ADDR = "{url}";
|
||||||
{INTERPRETER_JS}
|
{js}
|
||||||
{MAIN_JS}
|
{MAIN_JS}
|
||||||
main();
|
main();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in new issue
Block a user