Introduce network request handler (#4194)

* Impl NetworkMessageHandler

* Repalce Frontend fetch like messages with NetworkMessage

* Reimpl resource loading with NetworkMessage

* Fix wasm

* dedublicate resource requests

* Embedd font resources created by migration

* Fixup

* Fix tests

* Fix font catalog not loading

* Cleanup

* Fix layouts not updating when font catalog is loaded

* Review

* Review

* Cleanup
This commit is contained in:
Timon
2026-06-05 13:27:45 -04:00
committed by Keavon Chambers
parent cd8ea1f554
commit 08c6d02e5b
23 changed files with 348 additions and 224 deletions
+10
View File
@@ -0,0 +1,10 @@
mod network_message;
mod network_message_handler;
pub mod utility_types;
#[doc(inline)]
pub use network_message::{NetworkMessage, NetworkMessageDiscriminant};
#[doc(inline)]
pub use network_message_handler::{NetworkMessageContext, NetworkMessageHandler};
#[doc(inline)]
pub use utility_types::Client;
@@ -0,0 +1,49 @@
use std::pin::Pin;
use dyn_any::WasmNotSend;
use crate::messages::network::utility_types::Client;
use crate::messages::prelude::*;
#[impl_message(Message, Network)]
#[derive(derivative::Derivative, serde::Serialize, serde::Deserialize)]
#[derivative(Debug, PartialEq)]
pub enum NetworkMessage {
Request {
#[serde(skip, default)]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
request: Option<RequestFn>,
},
}
impl NetworkMessage {
pub fn request<F, Fut>(f: F) -> Self
where
F: FnOnce(Client) -> Fut + WasmNotSend + 'static,
Fut: Future<Output = Message> + WasmNotSend + 'static,
{
NetworkMessage::Request {
request: Some(Box::new(move |c| Box::pin(f(c)))),
}
}
}
#[cfg(not(target_family = "wasm"))]
type RequestFuture = Pin<Box<dyn Future<Output = Message> + Send>>;
#[cfg(target_family = "wasm")]
type RequestFuture = Pin<Box<dyn Future<Output = Message>>>;
#[cfg(not(target_family = "wasm"))]
type RequestFn = Box<dyn FnOnce(Client) -> RequestFuture + Send>;
#[cfg(target_family = "wasm")]
type RequestFn = Box<dyn FnOnce(Client) -> RequestFuture>;
impl Clone for NetworkMessage {
fn clone(&self) -> Self {
match self {
NetworkMessage::Request { .. } => {
log::error!("Cloning a NetworkMessage::Request is not supported");
NetworkMessage::Request { request: None }
}
}
}
}
@@ -0,0 +1,27 @@
use crate::messages::network::utility_types::Client;
use crate::messages::prelude::*;
#[derive(ExtractField)]
pub struct NetworkMessageContext {}
#[derive(Debug, Default, ExtractField)]
pub struct NetworkMessageHandler {
client: Client,
}
#[message_handler_data]
impl MessageHandler<NetworkMessage, NetworkMessageContext> for NetworkMessageHandler {
fn process_message(&mut self, message: NetworkMessage, responses: &mut VecDeque<Message>, _context: NetworkMessageContext) {
match message {
NetworkMessage::Request { request } => {
if let Some(request) = request {
responses.add(request(self.client.clone()));
} else {
log::error!("received a empty NetworkMessage::Request");
}
}
}
}
advertise_actions!(NetworkMessageDiscriminant;
);
}
@@ -0,0 +1,30 @@
use reqwest::IntoUrl;
#[derive(Debug, Clone)]
pub struct Client {
inner: Option<reqwest::Client>,
}
impl Default for Client {
fn default() -> Self {
Self {
#[cfg(not(target_family = "wasm"))]
inner: reqwest::Client::builder().timeout(std::time::Duration::from_secs(100)).build().ok(),
#[cfg(target_family = "wasm")]
inner: reqwest::Client::builder().build().ok(),
}
}
}
impl Client {
pub async fn fetch<U: IntoUrl>(&self, url: U) -> Option<Box<[u8]>> {
let Some(client) = &self.inner else {
log::error!("HTTP client failed to initialize, cannot fetch");
return None;
};
let response = client.get(url).send().await;
let response = response.and_then(|r| r.error_for_status()).map_err(|err| log::error!("failed to fetch: {err}")).ok()?;
let bytes = response.bytes().await.map_err(|err| log::error!("failed to read response body: {err}")).ok()?;
Some(bytes.to_vec().into_boxed_slice())
}
}