codify "platform" into resolution logic, server-fn/fullstack overhaul (#4596)
overhaul dioxus fullstack using a new implementation of server_fn This PR is a complete rewrite of dioxus fullstack designed to improve compatibility with axum and to fix a number of compilation errors based on feature flag unification plumbing through macros.
This commit is contained in:
1 parent
8421c3aef0
commit
f2b1c6d97f
424 files changed
+17284
-6624
No files matched your search
@@ -2,7 +2,7 @@ use crate::{
|
||||
config::{Config, WindowCloseBehaviour},
|
||||
edits::EditWebsocket,
|
||||
event_handlers::WindowEventHandlers,
|
||||
file_upload::{DesktopFileUploadForm, FileDialogRequest, NativeFileEngine},
|
||||
file_upload::{DesktopFileUploadForm, FileDialogRequest},
|
||||
ipc::{IpcMessage, UserWindowEvent},
|
||||
query::QueryResult,
|
||||
shortcut::ShortcutRegistry,
|
||||
@@ -16,7 +16,6 @@ use std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::HashMap,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tao::{
|
||||
@@ -420,9 +419,7 @@ impl App {
|
||||
let event_bubbles = file_dialog.bubbles;
|
||||
let files = file_dialog.get_file_event();
|
||||
|
||||
let as_any = Box::new(DesktopFileUploadForm {
|
||||
files: Arc::new(NativeFileEngine::new(files)),
|
||||
});
|
||||
let as_any = Box::new(DesktopFileUploadForm { files });
|
||||
|
||||
let data = Rc::new(PlatformEventData::new(as_any));
|
||||
|
||||
|
||||
@@ -354,7 +354,7 @@ fn is_main_thread() -> bool {
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct PendingDesktopContext {
|
||||
pub(crate) receiver: tokio::sync::oneshot::Receiver<DesktopContext>,
|
||||
pub(crate) receiver: futures_channel::oneshot::Receiver<DesktopContext>,
|
||||
}
|
||||
|
||||
impl PendingDesktopContext {
|
||||
@@ -366,9 +366,7 @@ impl PendingDesktopContext {
|
||||
}
|
||||
|
||||
/// Try to resolve the pending context into a [`DesktopContext`].
|
||||
pub async fn try_resolve(
|
||||
self,
|
||||
) -> Result<DesktopContext, tokio::sync::oneshot::error::RecvError> {
|
||||
pub async fn try_resolve(self) -> Result<DesktopContext, futures_channel::oneshot::Canceled> {
|
||||
self.receiver.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ use dioxus_html::{
|
||||
point_interaction::{
|
||||
InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
|
||||
},
|
||||
FileEngine, HasDragData, HasFileData, HasFormData, HasMouseData, SerializedMouseData,
|
||||
SerializedPointInteraction,
|
||||
FileData, HasDragData, HasFileData, HasFormData, HasMouseData, NativeFileData,
|
||||
SerializedMouseData, SerializedPointInteraction,
|
||||
};
|
||||
|
||||
use serde::Deserialize;
|
||||
@@ -155,12 +155,16 @@ impl FromStr for Filters {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct DesktopFileUploadForm {
|
||||
pub files: Arc<NativeFileEngine>,
|
||||
pub files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl HasFileData for DesktopFileUploadForm {
|
||||
fn files(&self) -> Option<Arc<dyn FileEngine>> {
|
||||
Some(self.files.clone())
|
||||
fn files(&self) -> Vec<FileData> {
|
||||
self.files
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|f| FileData::new(DesktopFileData(f)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,12 +191,16 @@ impl NativeFileHover {
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct DesktopFileDragEvent {
|
||||
pub mouse: SerializedPointInteraction,
|
||||
pub files: Arc<NativeFileEngine>,
|
||||
pub files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl HasFileData for DesktopFileDragEvent {
|
||||
fn files(&self) -> Option<Arc<dyn FileEngine>> {
|
||||
Some(self.files.clone())
|
||||
fn files(&self) -> Vec<FileData> {
|
||||
self.files
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|f| FileData::new(DesktopFileData(f)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,78 +256,93 @@ impl PointerInteraction for DesktopFileDragEvent {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NativeFileEngine {
|
||||
files: Vec<PathBuf>,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct DesktopFileData(pub(crate) PathBuf);
|
||||
|
||||
impl NativeFileEngine {
|
||||
pub fn new(files: Vec<PathBuf>) -> Self {
|
||||
Self { files }
|
||||
impl NativeFileData for DesktopFileData {
|
||||
fn name(&self) -> String {
|
||||
self.0.file_name().unwrap().to_string_lossy().into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl FileEngine for NativeFileEngine {
|
||||
fn files(&self) -> Vec<String> {
|
||||
self.files
|
||||
.iter()
|
||||
.filter_map(|f| Some(f.to_str()?.to_string()))
|
||||
.collect()
|
||||
fn size(&self) -> u64 {
|
||||
std::fs::metadata(&self.0).map(|m| m.len()).unwrap_or(0)
|
||||
}
|
||||
|
||||
async fn file_size(&self, file: &str) -> Option<u64> {
|
||||
#[cfg(feature = "tokio_runtime")]
|
||||
{
|
||||
let file = File::open(file).await.ok()?;
|
||||
Some(file.metadata().await.ok()?.len())
|
||||
}
|
||||
#[cfg(not(feature = "tokio_runtime"))]
|
||||
{
|
||||
None
|
||||
}
|
||||
fn last_modified(&self) -> u64 {
|
||||
std::fs::metadata(&self.0)
|
||||
.and_then(|m| m.modified())
|
||||
.ok()
|
||||
.and_then(|time| time.duration_since(std::time::UNIX_EPOCH).ok())
|
||||
.map(|duration| duration.as_secs())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
|
||||
#[cfg(feature = "tokio_runtime")]
|
||||
{
|
||||
let mut file = File::open(file).await.ok()?;
|
||||
fn read_bytes(
|
||||
&self,
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = Result<bytes::Bytes, dioxus_core::Error>> + 'static>,
|
||||
> {
|
||||
let path = self.0.clone();
|
||||
Box::pin(async move { Ok(bytes::Bytes::from(std::fs::read(&path)?)) })
|
||||
}
|
||||
|
||||
let mut contents = Vec::new();
|
||||
file.read_to_end(&mut contents).await.ok()?;
|
||||
fn read_string(
|
||||
&self,
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = Result<String, dioxus_core::Error>> + 'static>,
|
||||
> {
|
||||
let path = self.0.clone();
|
||||
Box::pin(async move { Ok(std::fs::read_to_string(&path)?) })
|
||||
}
|
||||
|
||||
Some(contents)
|
||||
}
|
||||
#[cfg(not(feature = "tokio_runtime"))]
|
||||
{
|
||||
None
|
||||
}
|
||||
fn inner(&self) -> &dyn std::any::Any {
|
||||
&self.0
|
||||
}
|
||||
|
||||
fn path(&self) -> PathBuf {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
async fn read_file_to_string(&self, file: &str) -> Option<String> {
|
||||
fn byte_stream(
|
||||
&self,
|
||||
) -> std::pin::Pin<
|
||||
Box<
|
||||
dyn futures_util::Stream<Item = Result<bytes::Bytes, dioxus_core::Error>>
|
||||
+ 'static
|
||||
+ Send,
|
||||
>,
|
||||
> {
|
||||
let path = self.0.clone();
|
||||
#[cfg(feature = "tokio_runtime")]
|
||||
{
|
||||
let mut file = File::open(file).await.ok()?;
|
||||
// todo!()
|
||||
// use futures_util::TryFutureExt;
|
||||
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents).await.ok()?;
|
||||
|
||||
Some(contents)
|
||||
}
|
||||
#[cfg(not(feature = "tokio_runtime"))]
|
||||
{
|
||||
None
|
||||
// futures_util::stream::try_unfold(File::open(path), |mut file| async move {
|
||||
// let mut buf = vec![0; 8192];
|
||||
// let n = file
|
||||
// .read(&mut buf)
|
||||
// .await
|
||||
// .map_err(|e| dioxus_core::Error::from(e))?;
|
||||
// if n == 0 {
|
||||
// Ok(None)
|
||||
// } else {
|
||||
// buf.truncate(n);
|
||||
// Ok(Some((bytes::Bytes::from(buf), file)))
|
||||
// }
|
||||
// })
|
||||
// .map(|res| res.map(bytes::Bytes::from))
|
||||
// .boxed()
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
|
||||
#[cfg(feature = "tokio_runtime")]
|
||||
{
|
||||
let file = File::open(file).await.ok()?;
|
||||
Some(Box::new(file))
|
||||
}
|
||||
#[cfg(not(feature = "tokio_runtime"))]
|
||||
{
|
||||
None
|
||||
}
|
||||
fn content_type(&self) -> Option<String> {
|
||||
Some(
|
||||
dioxus_asset_resolver::native::get_mime_from_ext(
|
||||
self.0.extension().and_then(|ext| ext.to_str()),
|
||||
)
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,9 @@ use crate::menubar::DioxusMenu;
|
||||
use crate::PendingDesktopContext;
|
||||
use crate::WindowCloseBehaviour;
|
||||
use crate::{
|
||||
app::SharedContext,
|
||||
assets::AssetHandlerRegistry,
|
||||
edits::WryQueue,
|
||||
file_upload::{NativeFileEngine, NativeFileHover},
|
||||
ipc::UserWindowEvent,
|
||||
protocol,
|
||||
waker::tao_waker,
|
||||
Config, DesktopContext, DesktopService,
|
||||
app::SharedContext, assets::AssetHandlerRegistry, edits::WryQueue,
|
||||
file_upload::NativeFileHover, ipc::UserWindowEvent, protocol, waker::tao_waker, Config,
|
||||
DesktopContext, DesktopService,
|
||||
};
|
||||
use crate::{document::DesktopDocument, WeakDesktopContext};
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
@@ -19,10 +14,9 @@ use dioxus_core::{Runtime, ScopeId, VirtualDom};
|
||||
use dioxus_document::Document;
|
||||
use dioxus_history::{History, MemoryHistory};
|
||||
use dioxus_hooks::to_owned;
|
||||
use dioxus_html::{HasFileData, HtmlEvent, PlatformEventData};
|
||||
use dioxus_html::{HtmlEvent, PlatformEventData};
|
||||
use futures_util::{pin_mut, FutureExt};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
use std::{cell::OnceCell, time::Duration};
|
||||
use std::{rc::Rc, task::Waker};
|
||||
use wry::{DragDropEvent, RequestAsyncResponder, WebContext, WebViewBuilder, WebViewId};
|
||||
@@ -90,7 +84,7 @@ impl WebviewEdits {
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!(
|
||||
"Error parsing user_event: {:?}.Contents: {:?}, raw: {:#?}",
|
||||
"Error parsing user_event: {:?}. \n Contents: {:?}, \nraw: {:#?}",
|
||||
err,
|
||||
String::from_utf8(request.body().to_vec()),
|
||||
request
|
||||
@@ -131,20 +125,17 @@ impl WebviewEdits {
|
||||
}
|
||||
dioxus_html::EventData::Drag(ref drag) => {
|
||||
// we want to override this with a native file engine, provided by the most recent drag event
|
||||
if drag.files().is_some() {
|
||||
let file_event = recent_file.current().unwrap();
|
||||
let paths = match file_event {
|
||||
wry::DragDropEvent::Enter { paths, .. } => paths,
|
||||
wry::DragDropEvent::Drop { paths, .. } => paths,
|
||||
_ => vec![],
|
||||
};
|
||||
Rc::new(PlatformEventData::new(Box::new(DesktopFileDragEvent {
|
||||
mouse: drag.mouse.clone(),
|
||||
files: Arc::new(NativeFileEngine::new(paths)),
|
||||
})))
|
||||
} else {
|
||||
data.into_any()
|
||||
}
|
||||
let file_event = recent_file.current().unwrap();
|
||||
let paths = match file_event {
|
||||
wry::DragDropEvent::Enter { paths, .. } => paths,
|
||||
wry::DragDropEvent::Drop { paths, .. } => paths,
|
||||
_ => vec![],
|
||||
};
|
||||
|
||||
Rc::new(PlatformEventData::new(Box::new(DesktopFileDragEvent {
|
||||
mouse: drag.mouse.clone(),
|
||||
files: paths,
|
||||
})))
|
||||
}
|
||||
_ => data.into_any(),
|
||||
};
|
||||
@@ -561,12 +552,12 @@ impl SynchronousEventResponse {
|
||||
pub(crate) struct PendingWebview {
|
||||
dom: VirtualDom,
|
||||
cfg: Config,
|
||||
sender: tokio::sync::oneshot::Sender<DesktopContext>,
|
||||
sender: futures_channel::oneshot::Sender<DesktopContext>,
|
||||
}
|
||||
|
||||
impl PendingWebview {
|
||||
pub(crate) fn new(dom: VirtualDom, cfg: Config) -> (Self, PendingDesktopContext) {
|
||||
let (sender, receiver) = tokio::sync::oneshot::channel();
|
||||
let (sender, receiver) = futures_channel::oneshot::channel();
|
||||
let webview = Self { dom, cfg, sender };
|
||||
let pending = PendingDesktopContext { receiver };
|
||||
(webview, pending)
|
||||
|
||||
Reference in new issue
Block a user