diff --git a/crates/polymodel-renderer-worker/src/worker.rs b/crates/polymodel-renderer-worker/src/worker.rs index 5156f21..70ffc9b 100644 --- a/crates/polymodel-renderer-worker/src/worker.rs +++ b/crates/polymodel-renderer-worker/src/worker.rs @@ -50,7 +50,7 @@ struct InputState { struct RendererState { context: three_d::Context, - mesh: three_d::Gm, + mesh: Option>, ambient_light: three_d::AmbientLight, key_light: three_d::DirectionalLight, fill_light: three_d::DirectionalLight, @@ -139,7 +139,6 @@ impl WorkerState { let (ambient_light, key_light, fill_light) = standard_lights(&context); - let placeholder = build_placeholder_mesh(&context); let viewport = three_d::Viewport::new_at_origo(self.pixel_width, self.pixel_height); let camera = three_d::Camera::new_perspective( viewport, @@ -155,7 +154,7 @@ impl WorkerState { self.canvas = Some(canvas); self.renderer = Some(RendererState { context, - mesh: placeholder, + mesh: None, ambient_light, key_light, fill_light, @@ -164,39 +163,9 @@ impl WorkerState { }); self.post_event(&RendererEvent::ContextAcquired); self.post_event(&RendererEvent::RendererReady); - self.needs_render = true; } } -fn build_placeholder_mesh( - context: &three_d::Context, -) -> three_d::Gm { - let mut mesh = three_d_asset::TriMesh { - positions: three_d_asset::Positions::F32(vec![ - three_d_asset::Vec3::new(0.0, 0.0, 0.18), - three_d_asset::Vec3::new(-0.16, -0.12, -0.08), - three_d_asset::Vec3::new(0.16, -0.12, -0.08), - three_d_asset::Vec3::new(0.0, 0.18, -0.08), - ]), - indices: three_d_asset::Indices::U32(vec![0, 1, 2, 0, 2, 3, 0, 3, 1, 1, 3, 2]), - normals: None, - tangents: None, - uvs: None, - colors: None, - }; - mesh.compute_normals(); - - three_d::Gm::new( - three_d::Mesh::new(context, &mesh), - three_d::PhysicalMaterial { - albedo: three_d::Srgba::new_opaque(116, 180, 255), - roughness: 0.72, - metallic: 0.0, - ..Default::default() - }, - ) -} - // --------------------------------------------------------------------------- // Scene building // --------------------------------------------------------------------------- @@ -384,19 +353,21 @@ fn consume_input_and_render(st: &mut WorkerState) { renderer.camera.set_viewport(viewport); let screen = three_d::RenderTarget::screen(&renderer.context, st.pixel_width, st.pixel_height); - let _ = screen - .clear(three_d::ClearState::color_and_depth( - 0.035, 0.043, 0.067, 1.0, 1.0, - )) - .render( + let screen = screen.clear(three_d::ClearState::color_and_depth( + 0.035, 0.043, 0.067, 1.0, 1.0, + )); + + if let Some(mesh) = &renderer.mesh { + let _ = screen.render( &renderer.camera, - std::slice::from_ref(&renderer.mesh), + std::slice::from_ref(mesh), &[ &renderer.ambient_light, &renderer.key_light, &renderer.fill_light, ], ); + } } // --------------------------------------------------------------------------- @@ -437,7 +408,7 @@ fn handle_load_mesh(state: &Rc>, format: MeshFormat, primar let ph = st.pixel_height; if let Some(renderer) = st.renderer.as_mut() { let (mesh, camera, control) = build_scene(&renderer.context, &model, pw, ph); - renderer.mesh = mesh; + renderer.mesh = Some(mesh); renderer.camera = camera; renderer.control = control; } diff --git a/src/browse.rs b/src/browse.rs index 87519ca..ef32c75 100644 --- a/src/browse.rs +++ b/src/browse.rs @@ -6,7 +6,6 @@ use crate::Route; use crate::examples::{HERO_SAMPLE_HANDLES, first_hero_sample_handle}; use crate::session::SessionIdentity; use crate::thing_card::ThingCard; -use crate::thing_detail::thing_detail_href; /// Cookie set client-side when the logged-out hero band is dismissed; read /// server-side so SSR omits the band on return without a hydration flash. @@ -209,8 +208,7 @@ pub(crate) fn Browse() -> Element { #[component] fn BrowseThingCard(thing: library::ThingViewBasic) -> Element { - let detail_href = thing_detail_href(&thing.uri); - rsx! { ThingCard { thing, detail_href } } + rsx! { ThingCard { thing } } } #[component] diff --git a/src/main.rs b/src/main.rs index f560912..ee9f500 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ mod session; mod shell; mod thing_card; mod thing_detail; +mod thing_routes; mod viewer; mod viewer_route; diff --git a/src/profile.rs b/src/profile.rs index e772614..fffa12c 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -2,7 +2,6 @@ use crate::PolymodelClient; use crate::Route; use crate::session::SessionIdentity; use crate::thing_card::ThingCard; -use crate::thing_detail::thing_detail_href; use dioxus::prelude::*; use jacquard_common::deps::smol_str::SmolStr; use jacquard_common::types::string::{Did, Handle, UriValue}; @@ -675,8 +674,7 @@ fn ProfileThingsGrid(things: Vec) -> Element { #[allow(clippy::useless_format)] #[component] fn ProfileThingCard(thing: library::ThingViewBasic) -> Element { - let detail_href = thing_detail_href(&thing.uri); - rsx! { ThingCard { thing, detail_href } } + rsx! { ThingCard { thing } } } fn list_item_count_label(count: i64) -> String { diff --git a/src/search.rs b/src/search.rs index 7007b9f..93a12f0 100644 --- a/src/search.rs +++ b/src/search.rs @@ -3,7 +3,6 @@ use polymodel_api::space_polymodel::library; use crate::Route; use crate::thing_card::ThingCard; -use crate::thing_detail::thing_detail_href; const SEARCH_LIMIT: i64 = 24; @@ -219,8 +218,7 @@ async fn search_seed( #[component] fn SearchThingCard(thing: library::ThingViewBasic) -> Element { - let detail_href = thing_detail_href(&thing.uri); - rsx! { ThingCard { thing, detail_href } } + rsx! { ThingCard { thing } } } fn search_pagination( diff --git a/src/thing_card.rs b/src/thing_card.rs index 37a699f..6b8ad9a 100644 --- a/src/thing_card.rs +++ b/src/thing_card.rs @@ -15,6 +15,7 @@ use polymodel_api::space_polymodel::library; use crate::author_byline::AuthorByline; use crate::client::PolymodelClient; use crate::session::SessionIdentity; +use crate::thing_routes::thing_detail_href; #[cfg(test)] const COMPLETE_PREVIEW_SVG: &str = "data:image/svg+xml,%3Csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 480\"%3E%3Crect width=\"640\" height=\"480\" fill=\"%23f4efe4\"/%3E%3Cg stroke=\"%232a6f97\" stroke-width=\"2\" fill=\"none\"%3E%3Cpath d=\"M64 96h512v288H64zM160 144h320v192H160zM256 192h128v96H256z\"/%3E%3Cpath d=\"M64 384 160 336M576 384 480 336M160 144 64 96M480 144 576 96\"/%3E%3C/g%3E%3Cg fill=\"%2366767f\" font-family=\"monospace\" font-size=\"24\"%3E%3Ctext x=\"72\" y=\"438\"%3Eparametric enclosure preview%3C/text%3E%3C/g%3E%3C/svg%3E"; @@ -126,13 +127,11 @@ pub fn thing_card_fixtures() -> Vec { #[allow(clippy::useless_format)] #[component] -pub fn ThingCard( - thing: library::ThingViewBasic, - detail_href: Option, -) -> Element { +pub fn ThingCard(thing: library::ThingViewBasic) -> Element { let client = use_context::(); let session = use_context::>(); let title = thing_title(&thing); + let detail_href = thing_detail_href(&thing.uri); let media = select_card_media(thing.cover.as_deref(), thing.previews.as_deref(), title); let stats = stats(&thing); let mut like_uri = use_signal(|| thing.viewer.like.clone()); @@ -173,7 +172,7 @@ pub fn ThingCard( div { class: "thing-card-placeholder blueprint-media", role: "img", aria_label: "Blueprint placeholder for missing preview media", span { class: "blueprint-axis blueprint-axis-x", "X" } span { class: "blueprint-axis blueprint-axis-y", "Y" } - span { class: "blueprint-dimension", "preview pending" } + span { class: "blueprint-dimension", "no preview image" } } } } @@ -184,7 +183,7 @@ pub fn ThingCard( div { class: "thing-card-placeholder blueprint-media", role: "img", aria_label: "Blueprint placeholder for missing preview media", span { class: "blueprint-axis blueprint-axis-x", "X" } span { class: "blueprint-axis blueprint-axis-y", "Y" } - span { class: "blueprint-dimension", "preview pending" } + span { class: "blueprint-dimension", "no preview image" } } } } @@ -254,7 +253,7 @@ pub fn ThingCard( action_pending.set(false); }); }, - "♡ {like_label}" + "{like_label}" } button { class: "thing-card-action", @@ -292,11 +291,11 @@ pub fn ThingCard( action_pending.set(false); }); }, - "☆ {save_label}" + "{save_label}" } } else { - a { class: "thing-card-action", href: "/oauth/start", aria_label: "Sign in to like {title}", "♡ Like" } - a { class: "thing-card-action", href: "/oauth/start", aria_label: "Sign in to save {title}", "☆ Save" } + a { class: "thing-card-action", href: "/oauth/start", aria_label: "Sign in to like {title}", "Like" } + a { class: "thing-card-action", href: "/oauth/start", aria_label: "Sign in to save {title}", "Save" } } if let Some(error) = action_error.read().as_ref() { span { class: "thing-card-action-error", role: "status", "{error}" } @@ -480,7 +479,10 @@ mod tests { thing_title(&view), ) .unwrap(); - assert_eq!(view.author.display_name.as_ref().map(AsRef::as_ref), Some("Maker Name")); + assert_eq!( + view.author.display_name.as_ref().map(AsRef::as_ref), + Some("Maker Name") + ); assert_eq!(view.author.handle.as_ref(), "maker.tools"); assert_eq!(media.url, COMPLETE_PREVIEW_SVG); assert_eq!(media.alt, "Cover alt"); diff --git a/src/thing_detail.rs b/src/thing_detail.rs index 351dffb..22d93eb 100644 --- a/src/thing_detail.rs +++ b/src/thing_detail.rs @@ -69,16 +69,6 @@ fn detail_state(pending: bool, result: Option<&Result>) } } -pub(crate) fn thing_detail_href(uri: &AtUri) -> Option { - let path = uri.path()?; - if path.collection.as_str() != "space.polymodel.library.thing" { - return None; - } - let repo = uri.authority(); - let rkey = path.rkey?; - Some(format!("/{}/thing/{}", repo.as_str(), rkey.as_ref())) -} - #[cfg_attr(not(feature = "server"), allow(dead_code))] fn part_download_href(uri: &AtUri) -> Option { let path = uri.path()?; diff --git a/src/thing_routes.rs b/src/thing_routes.rs new file mode 100644 index 0000000..0c4ff74 --- /dev/null +++ b/src/thing_routes.rs @@ -0,0 +1,11 @@ +use jacquard_common::types::string::AtUri; + +pub(crate) fn thing_detail_href(uri: &AtUri) -> Option { + let path = uri.path()?; + if path.collection.as_str() != "space.polymodel.library.thing" { + return None; + } + let repo = uri.authority(); + let rkey = path.rkey?; + Some(format!("/{}/thing/{}", repo.as_str(), rkey.as_ref())) +}