mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-27 09:54:41 -05:00
feat: local resources with .await
This commit is contained in:
@@ -84,4 +84,12 @@ impl SharedContext for CsrSharedContext {
|
||||
fn await_deferred(&self) -> Option<PinnedFuture<()>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_incomplete_chunk(&self, _id: SerializedDataId) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_incomplete_chunk(&self, _id: &SerializedDataId) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ extern "C" {
|
||||
static __RESOLVED_RESOURCES: Array;
|
||||
|
||||
static __SERIALIZED_ERRORS: Array;
|
||||
|
||||
static __INCOMPLETE_CHUNKS: Array;
|
||||
}
|
||||
|
||||
fn serialized_errors() -> Vec<(SerializedDataId, ErrorId, Error)> {
|
||||
@@ -38,6 +40,16 @@ fn serialized_errors() -> Vec<(SerializedDataId, ErrorId, Error)> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn incomplete_chunks() -> Vec<SerializedDataId> {
|
||||
__INCOMPLETE_CHUNKS
|
||||
.iter()
|
||||
.map(|value| {
|
||||
let id = value.as_f64().unwrap() as usize;
|
||||
SerializedDataId(id)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// An error that has been serialized across the network boundary.
|
||||
#[derive(Debug, Clone)]
|
||||
struct SerializedError(String);
|
||||
@@ -57,6 +69,7 @@ pub struct HydrateSharedContext {
|
||||
is_hydrating: AtomicBool,
|
||||
during_hydration: AtomicBool,
|
||||
errors: Lazy<Vec<(SerializedDataId, ErrorId, Error)>>,
|
||||
incomplete: Lazy<Vec<SerializedDataId>>,
|
||||
}
|
||||
|
||||
impl HydrateSharedContext {
|
||||
@@ -67,6 +80,7 @@ impl HydrateSharedContext {
|
||||
is_hydrating: AtomicBool::new(true),
|
||||
during_hydration: AtomicBool::new(true),
|
||||
errors: Lazy::new(serialized_errors),
|
||||
incomplete: Lazy::new(incomplete_chunks),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +94,7 @@ impl HydrateSharedContext {
|
||||
is_hydrating: AtomicBool::new(false),
|
||||
during_hydration: AtomicBool::new(true),
|
||||
errors: Lazy::new(serialized_errors),
|
||||
incomplete: Lazy::new(incomplete_chunks),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,4 +181,11 @@ impl SharedContext for HydrateSharedContext {
|
||||
fn await_deferred(&self) -> Option<PinnedFuture<()>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_incomplete_chunk(&self, _id: SerializedDataId) {}
|
||||
|
||||
fn get_incomplete_chunk(&self, id: &SerializedDataId) -> bool {
|
||||
self.incomplete.iter().any(|entry| entry == id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,4 +140,11 @@ pub trait SharedContext: Debug {
|
||||
///
|
||||
/// In browser implementations, this should be a no-op.
|
||||
fn await_deferred(&self) -> Option<PinnedFuture<()>>;
|
||||
|
||||
/// Tells the client that this chunk is being sent from the server before all its data have
|
||||
/// loaded, and it may be in a fallback state.
|
||||
fn set_incomplete_chunk(&self, id: SerializedDataId);
|
||||
|
||||
/// Checks whether this chunk is being sent from the server before all its data have loaded.
|
||||
fn get_incomplete_chunk(&self, id: &SerializedDataId) -> bool;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::{SerializedDataId, SharedContext};
|
||||
use crate::{PinnedFuture, PinnedStream};
|
||||
use futures::{
|
||||
future::join_all,
|
||||
stream::{self},
|
||||
stream::{self, once},
|
||||
Stream, StreamExt,
|
||||
};
|
||||
use or_poisoned::OrPoisoned;
|
||||
@@ -33,6 +33,7 @@ pub struct SsrSharedContext {
|
||||
errors: ErrorBuf,
|
||||
sealed_error_boundaries: SealedErrors,
|
||||
deferred: Mutex<Vec<PinnedFuture<()>>>,
|
||||
incomplete: Arc<Mutex<Vec<SerializedDataId>>>,
|
||||
}
|
||||
|
||||
impl SsrSharedContext {
|
||||
@@ -178,8 +179,19 @@ impl SharedContext for SsrSharedContext {
|
||||
sealed_error_boundaries: Arc::clone(&self.sealed_error_boundaries),
|
||||
};
|
||||
|
||||
let stream =
|
||||
stream::once(async move { initial_chunk }).chain(async_data);
|
||||
let incomplete = Arc::clone(&self.incomplete);
|
||||
|
||||
let stream = stream::once(async move { initial_chunk })
|
||||
.chain(async_data)
|
||||
.chain(once(async move {
|
||||
let mut script = String::new();
|
||||
script.push_str("__INCOMPLETE_CHUNKS=[");
|
||||
for chunk in mem::take(&mut *incomplete.lock().or_poisoned()) {
|
||||
_ = write!(script, "{},", chunk.0);
|
||||
}
|
||||
script.push_str("];");
|
||||
script
|
||||
}));
|
||||
Some(Box::pin(stream))
|
||||
}
|
||||
|
||||
@@ -203,6 +215,18 @@ impl SharedContext for SsrSharedContext {
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn set_incomplete_chunk(&self, id: SerializedDataId) {
|
||||
self.incomplete.lock().or_poisoned().push(id);
|
||||
}
|
||||
|
||||
fn get_incomplete_chunk(&self, id: &SerializedDataId) -> bool {
|
||||
self.incomplete
|
||||
.lock()
|
||||
.or_poisoned()
|
||||
.iter()
|
||||
.any(|entry| entry == id)
|
||||
}
|
||||
}
|
||||
|
||||
struct AsyncDataStream {
|
||||
|
||||
Reference in New Issue
Block a user