Fix bundling/path errors, sidecar support, iOS/Android simulator support, asset hotreload fixes, serverfn hotrelad, native tailwind support (#2779)
* wip: fix manganis import path * upgrade tauri bundler, convert todo!() to unimplemented!() * feat: mobile hotreloading + ios asset configuration * remove oid demo and its associated env vars * nuke all the things that cause cache thrashing * swap to Asset type * add some more logs * display impl for attrvalue * dont panic on collect failure * cut down web deps to 150 * clean up deps in a lot of places, simplify build scripts * clean up asset resolution and cli-dev profile * wire up absolute paths for manganis asset in a particular mode * move document related things around to shorten compile times * move most things to `document::Item` and then decompose the cli-config * switch link to stylesheet for clarity in examples * move manganis workspace example to examples folder * decompose manganis-cli-support * dont need mobile demo anymore * remove gloo dialogs * html doesnt need document * rename hotreload to devtools * really clean up the html crate * fix weird rsx spacing * clean up desktop to use callback * clean up document a bit * re-wire up devsocket * fix utf css * yeeeet that js out of here * synthetic web system works * web crate almost done being cleaned up * desktop mostly cleaned up too * clean up cli a bit * more cli cleanup * cli builds again * clean up cli, inline structs, cut down number of unique types where possible * hotreload works again for desktop and this time, mobile too * cfg out tungstenite * devserver err * more refactor to cli builder * switch to unbounded_send * new structure is much cleaner * add http serve subcommand * bundled hotreload * kick stylsheets * clean up a bit more, split up eventloop * amazingly serve seems done * change from pub to pub(crate) in cli * remove tools * bit more polish to cli * fix issue with join * gracefully handle fullstack without a server * fullstack mobile demo * fix launch function, move projects into example-projects folder * hoist examples * add "run" command * clean up launch * remove old manual websocket receiver * doctor command * allow desktop to scroll * cut apart router crate * dont put launch in prelude * use dioxus::launch where possible * rename rsx, cut out hotreload tests * remove liveview project * bump native * fix compile for renderers * move sync event response out of interpreter * move render in serve * rollback settings change * cli compiles, huzzah! * change uris for asset * fix asset * new tui screen * new cargo-like tui works * very very verty close * it works! very small bug with incorrect grapheme calc * Clean up devserver a bit * status sytem * tidy up debug filters * clean up logging situation * Fix a number of bugs with log printing * new printing system is more reliable * wire up more stuff * things working but fullstack is having issues * fullstack works again! * hotreloading bundled assets works again * bundled hotreload and beginnings of macos bundling * Hotreload desktop * combined server builds * add build handle * fix fullstack assets * make open async, add some hooks for ios * migrate filemap to runner * wip global crate system * fixup bundles + organize asset * fix asset location bug * all the bundled reloading! * open ios simulator! * full hotreload support for mobile + serverfn * basic cleanups * clean up dx * Move filemap * fix cutting of newlines * assets workibg, some android * hoist wry/tao * use sync locks and headers to fix issues with android * desktop -> mac/win/linux with alias * better logging * feat: workspace (entire computer!) hotreload * should rebuild toggle, ios simulator bootup * proper mobile support in launch * more robust handling of assets * fix cargo * bring back some of tauri bundle * make warnings go away, clippy happy on cli * some final clippy cleanups * fmt * move manganis to its own folder * upgrade bundle to stable * drastically slim down manganis, prepping for merge * typos, failing test, docsrs config * remove static gen test * nix static gen test * we use --platform web instead of --platform fullstack now * only bind dev urls in desktop/mobile * install gtk * nix static gen * split build dir by app name
This commit is contained in:
1 parent
ef436e4ed0
commit
7ec3453ca3
156 files changed
+11544
-9005
No files matched your search
@@ -0,0 +1,11 @@
|
||||
/// This is a hack to get around the fact that wry is currently not thread safe on android
|
||||
///
|
||||
/// We want to acquire this mutex before doing anything with the virtualdom directly
|
||||
pub fn android_runtime_lock() -> std::sync::MutexGuard<'static, ()> {
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::sync::Mutex;
|
||||
|
||||
static RUNTIME_LOCK: OnceCell<Mutex<()>> = OnceCell::new();
|
||||
|
||||
RUNTIME_LOCK.get_or_init(|| Mutex::new(())).lock().unwrap()
|
||||
}
|
||||
@@ -226,8 +226,9 @@ impl App {
|
||||
pub fn handle_initialize_msg(&mut self, id: WindowId) {
|
||||
let view = self.webviews.get_mut(&id).unwrap();
|
||||
|
||||
view.dom
|
||||
.rebuild(&mut *view.edits.wry_queue.mutation_state_mut());
|
||||
view.edits
|
||||
.wry_queue
|
||||
.with_mutation_state_mut(|f| view.dom.rebuild(f));
|
||||
|
||||
view.edits.wry_queue.send_edits();
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
use std::{cell::RefCell, collections::VecDeque, rc::Rc, task::Waker};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
sync::{Arc, RwLock},
|
||||
task::Waker,
|
||||
};
|
||||
|
||||
use dioxus_interpreter_js::MutationState;
|
||||
|
||||
@@ -6,7 +10,7 @@ use dioxus_interpreter_js::MutationState;
|
||||
/// It will hold onto the requests until the interpreter is ready to handle them and hold onto any pending edits until a new request is made.
|
||||
#[derive(Default, Clone)]
|
||||
pub(crate) struct WryQueue {
|
||||
inner: Rc<RefCell<WryQueueInner>>,
|
||||
inner: Arc<RwLock<WryQueueInner>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -23,7 +27,7 @@ pub(crate) struct WryQueueInner {
|
||||
|
||||
impl WryQueue {
|
||||
pub fn handle_request(&self, responder: wry::RequestAsyncResponder) {
|
||||
let mut myself = self.inner.borrow_mut();
|
||||
let mut myself = self.inner.write().unwrap();
|
||||
if let Some(bytes) = myself.edit_queue.pop_back() {
|
||||
responder.respond(wry::http::Response::new(bytes));
|
||||
} else {
|
||||
@@ -36,13 +40,17 @@ impl WryQueue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mutation_state_mut(&self) -> std::cell::RefMut<'_, MutationState> {
|
||||
std::cell::RefMut::map(self.inner.borrow_mut(), |myself| &mut myself.mutation_state)
|
||||
pub fn with_mutation_state_mut<O: 'static>(
|
||||
&self,
|
||||
f: impl FnOnce(&mut MutationState) -> O,
|
||||
) -> O {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
f(&mut inner.mutation_state)
|
||||
}
|
||||
|
||||
/// Send a list of mutations to the webview
|
||||
pub(crate) fn send_edits(&self) {
|
||||
let mut myself = self.inner.borrow_mut();
|
||||
let mut myself = self.inner.write().unwrap();
|
||||
let serialized_edits = myself.mutation_state.export_memory();
|
||||
// There are pending edits that need to be applied to the webview before we run futures
|
||||
myself.edits_in_progress = true;
|
||||
@@ -54,13 +62,13 @@ impl WryQueue {
|
||||
}
|
||||
|
||||
fn edits_in_progress(&self) -> bool {
|
||||
self.inner.borrow().edits_in_progress
|
||||
self.inner.read().unwrap().edits_in_progress
|
||||
}
|
||||
|
||||
/// Wait until all pending edits have been rendered in the webview
|
||||
pub fn poll_edits_flushed(&self, cx: &mut std::task::Context<'_>) -> std::task::Poll<()> {
|
||||
if self.edits_in_progress() {
|
||||
let mut myself = self.inner.borrow_mut();
|
||||
let mut myself = self.inner.write().unwrap();
|
||||
let waker = cx.waker();
|
||||
myself.waiting_for_edits_flushed.push(waker.clone());
|
||||
std::task::Poll::Pending
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Dioxus app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<!-- CUSTOM HEAD -->
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
mod android_sync_lock;
|
||||
mod app;
|
||||
mod assets;
|
||||
mod config;
|
||||
|
||||
+123
-220
@@ -2,12 +2,7 @@ use crate::document::NATIVE_EVAL_JS;
|
||||
use crate::{assets::*, webview::WebviewEdits};
|
||||
use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS;
|
||||
use dioxus_interpreter_js::NATIVE_JS;
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
path::{Component, Path, PathBuf},
|
||||
process::Command,
|
||||
sync::OnceLock,
|
||||
};
|
||||
use std::path::{Path, PathBuf};
|
||||
use wry::{
|
||||
http::{status::StatusCode, Request, Response},
|
||||
RequestAsyncResponder, Result,
|
||||
@@ -27,6 +22,96 @@ const EVENTS_PATH: &str = "dioxus://index.html/__events";
|
||||
|
||||
static DEFAULT_INDEX: &str = include_str!("./index.html");
|
||||
|
||||
#[allow(clippy::too_many_arguments)] // just for now, should fix this eventually
|
||||
/// Handle a request from the webview
|
||||
///
|
||||
/// - Tries to stream edits if they're requested.
|
||||
/// - If that doesn't match, tries a user provided asset handler
|
||||
/// - If that doesn't match, tries to serve a file from the filesystem
|
||||
pub(super) fn desktop_handler(
|
||||
request: Request<Vec<u8>>,
|
||||
asset_handlers: AssetHandlerRegistry,
|
||||
responder: RequestAsyncResponder,
|
||||
edit_state: &WebviewEdits,
|
||||
custom_head: Option<String>,
|
||||
custom_index: Option<String>,
|
||||
root_name: &str,
|
||||
headless: bool,
|
||||
) {
|
||||
// Try to serve the index file first
|
||||
if let Some(index_bytes) =
|
||||
index_request(&request, custom_head, custom_index, root_name, headless)
|
||||
{
|
||||
return responder.respond(index_bytes);
|
||||
}
|
||||
|
||||
// If the request is asking for edits (ie binary protocol streaming), do that
|
||||
let trimmed_uri = request.uri().path().trim_matches('/');
|
||||
if trimmed_uri == "__edits" {
|
||||
return edit_state.wry_queue.handle_request(responder);
|
||||
}
|
||||
|
||||
// If the request is asking for an event response, do that
|
||||
if trimmed_uri == "__events" {
|
||||
return edit_state.handle_event(request, responder);
|
||||
}
|
||||
|
||||
// todo: we want to move the custom assets onto a different protocol or something
|
||||
if let Some(name) = request.uri().path().split('/').next() {
|
||||
if asset_handlers.has_handler(name) {
|
||||
let _name = name.to_string();
|
||||
return asset_handlers.handle_request(&_name, request, responder);
|
||||
}
|
||||
}
|
||||
|
||||
match serve_asset(request) {
|
||||
Ok(res) => responder.respond(res),
|
||||
Err(_e) => responder.respond(
|
||||
Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(String::from("Failed to serve asset").into_bytes())
|
||||
.unwrap(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn serve_asset(request: Request<Vec<u8>>) -> Result<Response<Vec<u8>>> {
|
||||
// If the user provided a custom asset handler, then call it and return the response if the request was handled.
|
||||
// The path is the first part of the URI, so we need to trim the leading slash.
|
||||
let mut uri_path = PathBuf::from(
|
||||
urlencoding::decode(request.uri().path())
|
||||
.expect("expected URL to be UTF-8 encoded")
|
||||
.as_ref(),
|
||||
);
|
||||
|
||||
// If the asset doesn't exist, or starts with `/assets/`, then we'll try to serve out of the bundle
|
||||
// This lets us handle both absolute and relative paths without being too "special"
|
||||
// It just means that our macos bundle is a little "special" because we need to place an `assets`
|
||||
// dir in the `Resources` dir.
|
||||
//
|
||||
// If there's no asset root, we use the cargo manifest dir as the root, or the current dir
|
||||
if !uri_path.exists() || uri_path.starts_with("/assets/") {
|
||||
let bundle_root = get_asset_root();
|
||||
let relative_path = uri_path.strip_prefix("/").unwrap();
|
||||
uri_path = bundle_root.join(relative_path);
|
||||
println!("bundle root: {bundle_root:?}");
|
||||
println!("uri path: {uri_path:?}");
|
||||
}
|
||||
|
||||
// If the asset exists, then we can serve it!
|
||||
if uri_path.exists() {
|
||||
let mime_type = get_mime_from_path(&uri_path);
|
||||
return Ok(Response::builder()
|
||||
.header("Content-Type", mime_type?)
|
||||
.header("Access-Control-Allow-Origin", "*")
|
||||
.body(std::fs::read(uri_path)?)?);
|
||||
}
|
||||
|
||||
Ok(Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(String::from("Not Found").into_bytes())?)
|
||||
}
|
||||
|
||||
/// Build the index.html file we use for bootstrapping a new app
|
||||
///
|
||||
/// We use wry/webview by building a special index.html that forms a bridge between the webview and your rust code
|
||||
@@ -36,7 +121,7 @@ static DEFAULT_INDEX: &str = include_str!("./index.html");
|
||||
/// mess with UI elements. We make this decision since other renderers like LiveView are very separate and can
|
||||
/// never properly bridge the gap. Eventually of course, the idea is to build a custom CSS/HTML renderer where you
|
||||
/// *do* have native control over elements, but that still won't work with liveview.
|
||||
pub(super) fn index_request(
|
||||
fn index_request(
|
||||
request: &Request<Vec<u8>>,
|
||||
custom_head: Option<String>,
|
||||
custom_index: Option<String>,
|
||||
@@ -53,17 +138,8 @@ pub(super) fn index_request(
|
||||
|
||||
// Insert a custom head if provided
|
||||
// We look just for the closing head tag. If a user provided a custom index with weird syntax, this might fail
|
||||
let head = match custom_head {
|
||||
Some(mut head) => {
|
||||
if let Some(assets_head) = assets_head() {
|
||||
head.push_str(&assets_head);
|
||||
}
|
||||
Some(head)
|
||||
}
|
||||
None => assets_head(),
|
||||
};
|
||||
|
||||
if let Some(head) = head {
|
||||
if let Some(head) = custom_head {
|
||||
index.insert_str(index.find("</head>").expect("Head element to exist"), &head);
|
||||
}
|
||||
|
||||
@@ -82,141 +158,6 @@ pub(super) fn index_request(
|
||||
.ok()
|
||||
}
|
||||
|
||||
fn assets_head() -> Option<String> {
|
||||
#[cfg(any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
{
|
||||
let assets_head_path = PathBuf::from("__assets_head.html");
|
||||
let head = resolve_resource(&assets_head_path);
|
||||
match std::fs::read_to_string(&head) {
|
||||
Ok(s) => Some(s),
|
||||
Err(err) => {
|
||||
tracing::warn!("Assets built with manganis cannot be preloaded (failed to read {head:?}). This warning may occur when you build a desktop application without the dioxus CLI. If you do not use manganis, you can ignore this warning: {err}.");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
)))]
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_resource(path: &Path) -> PathBuf {
|
||||
let mut base_path = get_asset_root_or_default();
|
||||
|
||||
if running_in_dev_mode() {
|
||||
base_path.push(path);
|
||||
|
||||
// Special handler for Manganis filesystem fallback.
|
||||
// We need this since Manganis provides assets from workspace root.
|
||||
if !base_path.exists() {
|
||||
let workspace_root = get_workspace_root_from_cargo();
|
||||
let asset_path = workspace_root.join(path);
|
||||
return asset_path;
|
||||
}
|
||||
} else {
|
||||
let mut resource_path = PathBuf::new();
|
||||
for component in path.components() {
|
||||
// Tauri-bundle inserts special path segments for abnormal component paths
|
||||
match component {
|
||||
Component::Prefix(_) => {}
|
||||
Component::RootDir => resource_path.push("_root_"),
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => resource_path.push("_up_"),
|
||||
Component::Normal(p) => resource_path.push(p),
|
||||
}
|
||||
}
|
||||
base_path.push(resource_path);
|
||||
}
|
||||
base_path
|
||||
}
|
||||
|
||||
/// Handle a request from the webview
|
||||
///
|
||||
/// - Tries to stream edits if they're requested.
|
||||
/// - If that doesn't match, tries a user provided asset handler
|
||||
/// - If that doesn't match, tries to serve a file from the filesystem
|
||||
pub(super) fn desktop_handler(
|
||||
request: Request<Vec<u8>>,
|
||||
asset_handlers: AssetHandlerRegistry,
|
||||
responder: RequestAsyncResponder,
|
||||
edit_state: &WebviewEdits,
|
||||
) {
|
||||
// If the request is asking for edits (ie binary protocol streaming), do that
|
||||
let trimmed_uri = request.uri().path().trim_matches('/');
|
||||
if trimmed_uri == "__edits" {
|
||||
return edit_state.wry_queue.handle_request(responder);
|
||||
}
|
||||
|
||||
// If the request is asking for an event response, do that
|
||||
if trimmed_uri == "__events" {
|
||||
return edit_state.handle_event(request, responder);
|
||||
}
|
||||
|
||||
// If the user provided a custom asset handler, then call it and return the response if the request was handled.
|
||||
// The path is the first part of the URI, so we need to trim the leading slash.
|
||||
let path = PathBuf::from(
|
||||
urlencoding::decode(request.uri().path().trim_start_matches('/'))
|
||||
.expect("expected URL to be UTF-8 encoded")
|
||||
.as_ref(),
|
||||
);
|
||||
|
||||
if path.parent().is_none() {
|
||||
return tracing::error!("Asset request has no parent {path:?}");
|
||||
}
|
||||
|
||||
if let Some(name) = path.iter().next().unwrap().to_str() {
|
||||
if asset_handlers.has_handler(name) {
|
||||
return asset_handlers.handle_request(name, request, responder);
|
||||
}
|
||||
}
|
||||
|
||||
// Else, try to serve a file from the filesystem.
|
||||
match serve_from_fs(path) {
|
||||
Ok(res) => responder.respond(res),
|
||||
Err(e) => {
|
||||
tracing::error!("Error serving request from filesystem {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serve_from_fs(path: PathBuf) -> Result<Response<Vec<u8>>> {
|
||||
// If the path is relative, we'll try to serve it from the assets directory.
|
||||
let mut asset = resolve_resource(&path);
|
||||
|
||||
// If we can't find it, make it absolute and try again
|
||||
if !asset.exists() {
|
||||
asset = PathBuf::from("/").join(&path);
|
||||
}
|
||||
|
||||
if !asset.exists() {
|
||||
return Ok(Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(String::from("Not Found").into_bytes())?);
|
||||
}
|
||||
|
||||
Ok(Response::builder()
|
||||
.header("Content-Type", get_mime_from_path(&asset)?)
|
||||
.header("Access-Control-Allow-Origin", "*")
|
||||
.body(std::fs::read(asset)?)?)
|
||||
}
|
||||
|
||||
/// Construct the inline script that boots up the page and bridges the webview with rust code.
|
||||
///
|
||||
/// The arguments here:
|
||||
@@ -254,109 +195,71 @@ fn module_loader(root_id: &str, headless: bool) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the asset directory, following tauri/cargo-bundles directory discovery approach
|
||||
///
|
||||
/// Defaults to the current directory if no asset directory is found, which is useful for development when the app
|
||||
/// isn't bundled.
|
||||
fn get_asset_root_or_default() -> PathBuf {
|
||||
get_asset_root().unwrap_or_else(|| std::env::current_dir().unwrap())
|
||||
}
|
||||
|
||||
fn running_in_dev_mode() -> bool {
|
||||
// If running under cargo, there's no bundle!
|
||||
// There might be a smarter/more resilient way of doing this
|
||||
std::env::var_os("CARGO").is_some()
|
||||
}
|
||||
|
||||
/// Get the asset directory, following tauri/cargo-bundles directory discovery approach
|
||||
///
|
||||
/// Currently supports:
|
||||
/// - [x] macOS
|
||||
/// - [ ] Windows
|
||||
/// - [x] iOS
|
||||
/// - [x] Windows
|
||||
/// - [x] Linux (appimage)
|
||||
/// - [ ] Linux (rpm)
|
||||
/// - [ ] Linux (deb)
|
||||
/// - [ ] iOS
|
||||
/// - [ ] Android
|
||||
#[allow(unreachable_code)]
|
||||
fn get_asset_root() -> Option<PathBuf> {
|
||||
if running_in_dev_mode() {
|
||||
// todo: we don't want to canonicalize assets like this, but it will take longer to migrate, so we'll do it later
|
||||
// we should just be parsing paths the way they are instead of relative to the "asset" dir
|
||||
// manganis will eventually just dump the raw path to us, but until then, we need to canonicalize here
|
||||
return dioxus_cli_config::base_path();
|
||||
}
|
||||
fn get_asset_root() -> PathBuf {
|
||||
let cur_exe = std::env::current_exe().unwrap();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let bundle = core_foundation::bundle::CFBundle::main_bundle();
|
||||
let bundle_path = bundle.path()?;
|
||||
let resources_path = bundle.resources_path()?;
|
||||
let absolute_resources_root = bundle_path.join(resources_path);
|
||||
return dunce::canonicalize(absolute_resources_root).ok();
|
||||
return cur_exe
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("Resources");
|
||||
}
|
||||
|
||||
None
|
||||
// For all others, the structure looks like this:
|
||||
// app.(exe/appimage)
|
||||
// main.exe
|
||||
// assets/
|
||||
cur_exe.parent().unwrap().to_path_buf()
|
||||
}
|
||||
|
||||
/// Get the mime type from a path-like string
|
||||
fn get_mime_from_path(trimmed: &Path) -> Result<&'static str> {
|
||||
if trimmed.extension().is_some_and(|ext| ext == "svg") {
|
||||
fn get_mime_from_path(asset: &Path) -> Result<&'static str> {
|
||||
if asset.extension().is_some_and(|ext| ext == "svg") {
|
||||
return Ok("image/svg+xml");
|
||||
}
|
||||
|
||||
match infer::get_from_path(trimmed)?.map(|f| f.mime_type()) {
|
||||
match infer::get_from_path(asset)?.map(|f| f.mime_type()) {
|
||||
Some(f) if f != "text/plain" => Ok(f),
|
||||
_ => Ok(get_mime_by_ext(trimmed)),
|
||||
_other => Ok(get_mime_by_ext(asset)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the mime type from a URI using its extension
|
||||
fn get_mime_by_ext(trimmed: &Path) -> &'static str {
|
||||
match trimmed.extension().and_then(|e| e.to_str()) {
|
||||
// The common assets are all utf-8 encoded
|
||||
Some("js") => "text/javascript; charset=utf-8",
|
||||
Some("css") => "text/css; charset=utf-8",
|
||||
Some("json") => "application/json; charset=utf-8",
|
||||
Some("svg") => "image/svg+xml; charset=utf-8",
|
||||
Some("html") => "text/html; charset=utf-8",
|
||||
|
||||
// the rest... idk? probably not
|
||||
Some("mjs") => "text/javascript; charset=utf-8",
|
||||
Some("bin") => "application/octet-stream",
|
||||
Some("css") => "text/css",
|
||||
Some("csv") => "text/csv",
|
||||
Some("html") => "text/html",
|
||||
Some("ico") => "image/vnd.microsoft.icon",
|
||||
Some("js") => "text/javascript",
|
||||
Some("json") => "application/json",
|
||||
Some("jsonld") => "application/ld+json",
|
||||
Some("mjs") => "text/javascript",
|
||||
Some("rtf") => "application/rtf",
|
||||
Some("svg") => "image/svg+xml",
|
||||
Some("mp4") => "video/mp4",
|
||||
// Assume HTML when a TLD is found for eg. `dioxus:://dioxuslabs.app` | `dioxus://hello.com`
|
||||
Some(_) => "text/html",
|
||||
Some(_) => "text/html; charset=utf-8",
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
||||
// using octet stream according to this:
|
||||
None => "application/octet-stream",
|
||||
}
|
||||
}
|
||||
|
||||
/// A global that stores the workspace root. Used in [`get_workspace_root_from_cargo`].
|
||||
static WORKSPACE_ROOT: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
/// Describes the metadata we need from `cargo metadata`.
|
||||
#[derive(Deserialize)]
|
||||
struct CargoMetadata {
|
||||
workspace_root: PathBuf,
|
||||
}
|
||||
|
||||
/// Get the workspace root using `cargo metadata`. Should not be used in release mode.
|
||||
pub(crate) fn get_workspace_root_from_cargo() -> PathBuf {
|
||||
WORKSPACE_ROOT
|
||||
.get_or_init(|| {
|
||||
let out = Command::new("cargo")
|
||||
.args(["metadata", "--format-version", "1", "--no-deps"])
|
||||
.output()
|
||||
.expect("`cargo metadata` failed to run");
|
||||
|
||||
let out =
|
||||
String::from_utf8(out.stdout).expect("failed to parse output of `cargo metadata`");
|
||||
let metadata = serde_json::from_str::<CargoMetadata>(&out)
|
||||
.expect("failed to deserialize data from `cargo metadata`");
|
||||
|
||||
metadata.workspace_root
|
||||
})
|
||||
.to_owned()
|
||||
}
|
||||
@@ -55,10 +55,26 @@ impl WebviewEdits {
|
||||
&self,
|
||||
request: wry::http::Request<Vec<u8>>,
|
||||
) -> Result<Vec<u8>, serde_json::Error> {
|
||||
let response = match serde_json::from_slice(request.body()) {
|
||||
Ok(event) => self.handle_html_event(event),
|
||||
let data_from_header = request
|
||||
.headers()
|
||||
.get("dioxus-data")
|
||||
.map(|f| f.as_bytes())
|
||||
.expect("dioxus-data header is not a string");
|
||||
|
||||
let response = match serde_json::from_slice(data_from_header) {
|
||||
Ok(event) => {
|
||||
// we need to wait for the mutex lock to let us munge the main thread..
|
||||
let _lock = crate::android_sync_lock::android_runtime_lock();
|
||||
self.handle_html_event(event)
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Error parsing user_event: {:?}", err);
|
||||
tracing::error!("cannot decippher format of user event");
|
||||
tracing::error!(
|
||||
"Error parsing user_event: {:?}.Contents: {:?}, raw: {:#?}",
|
||||
err,
|
||||
String::from_utf8(request.body().to_vec()),
|
||||
request
|
||||
);
|
||||
SynchronousEventResponse::new(false)
|
||||
}
|
||||
};
|
||||
@@ -206,19 +222,16 @@ impl WebviewInstance {
|
||||
edits
|
||||
];
|
||||
move |request, responder: RequestAsyncResponder| {
|
||||
// Try to serve the index file first
|
||||
if let Some(index_bytes) = protocol::index_request(
|
||||
&request,
|
||||
protocol::desktop_handler(
|
||||
request,
|
||||
asset_handlers.clone(),
|
||||
responder,
|
||||
&edits,
|
||||
custom_head.clone(),
|
||||
custom_index.clone(),
|
||||
&root_name,
|
||||
headless,
|
||||
) {
|
||||
return responder.respond(index_bytes);
|
||||
}
|
||||
|
||||
// Otherwise, try to serve an asset, either from the user or the filesystem
|
||||
protocol::desktop_handler(request, asset_handlers.clone(), responder, &edits);
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -417,8 +430,12 @@ impl WebviewInstance {
|
||||
}
|
||||
}
|
||||
|
||||
self.dom
|
||||
.render_immediate(&mut *self.edits.wry_queue.mutation_state_mut());
|
||||
// lock the hack-ed in lock sync wry has some thread-safety issues with event handlers
|
||||
let _lock = crate::android_sync_lock::android_runtime_lock();
|
||||
|
||||
self.edits
|
||||
.wry_queue
|
||||
.with_mutation_state_mut(|f| self.dom.render_immediate(f));
|
||||
self.edits.wry_queue.send_edits();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user