use super::{
planes::{ReconstructedPlanesInput, apply_reconstructed_planes},
shared::{
catalogue_material_guess, format_optional_proportion, sides_from_rows, standard_codes,
},
};
use crate::{
AngleItem, DiagramDetailData, FileItem, LibraryModel, MainWindow, TiltModel, ViewportModel,
bridge::{
library::source::{self as library_source, LibrarySource},
render_thread::RenderContext,
},
gui::{
render::camera_lighting::resubmit_live_solid,
solid_preview::preview_state::SolidPreviewState,
},
settings::WorkerSettings,
};
use indicatrix_net::library::{DesignRecord, LibraryRequest, LibraryResponse};
use slint::{ComponentHandle, ModelRc, VecModel};
use std::sync::{Arc, Mutex};
use tracing::error;
pub(super) fn load_diagram_detail_remote(
ui: &MainWindow,
worker: WorkerSettings,
render_ctx: &Arc<Mutex<RenderContext>>,
entry_id: i64,
preview_state: &Arc<SolidPreviewState>,
source: Arc<Mutex<LibrarySource>>,
seq: u64,
) {
let render_ctx = render_ctx.clone();
let preview_state = Arc::clone(preview_state);
let request_source = LibrarySource::Remote(worker.clone());
library_source::spawn_library_request(
ui.as_weak(),
worker,
LibraryRequest::FetchDesign { entry_id },
move |ui, result| {
let current_source = source
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone();
if !reply_is_current(
crate::gui::library::search::is_current_search(seq),
&request_source,
¤t_source,
) {
return;
}
match result {
Ok(LibraryResponse::Design(record)) => {
apply_design_record_to_ui(ui, &render_ctx, &record, &preview_state);
}
Ok(LibraryResponse::NotFound) => {
ui.global::<LibraryModel>().set_status_message(
"Diagram detail not found on the remote library.".into(),
);
}
Ok(_) => {
ui.global::<LibraryModel>().set_status_message(
"Unexpected reply fetching remote diagram detail.".into(),
);
}
Err(e) => {
error!("Remote FetchDesign failed: {e}");
ui.global::<LibraryModel>()
.set_status_message(format!("Error loading remote detail: {e}").into());
}
}
},
);
}
#[must_use]
fn reply_is_current(
seq_is_current: bool,
request_source: &LibrarySource,
current_source: &LibrarySource,
) -> bool {
seq_is_current && request_source == current_source
}
fn apply_design_record_to_ui(
ui: &MainWindow,
render_ctx: &Arc<Mutex<RenderContext>>,
record: &DesignRecord,
preview_state: &Arc<SolidPreviewState>,
) {
let is_local = record.url.starts_with("local://");
let detail_data = DiagramDetailData {
id: record.entry_id as i32,
title: record.title.clone().into(),
url: record.url.clone().into(),
designer: record.designer_info.clone().unwrap_or_default().into(),
shape: record.shape.clone().unwrap_or_default().into(),
gear: record.index_gear.clone().unwrap_or_default().into(),
facets: record.facets_count.clone().unwrap_or_default().into(),
lw_ratio: format_optional_proportion(record.lw_ratio.as_deref()).into(),
material_guess: catalogue_material_guess(record.refractive_index.as_deref()).into(),
ri: record.refractive_index.clone().unwrap_or_default().into(),
volume: format_optional_proportion(record.volume.as_deref()).into(),
competition: record
.competition_diagram
.clone()
.unwrap_or_default()
.into(),
image_name: record.diagram_image_name.clone().unwrap_or_default().into(),
has_image: record.diagram_image_data.is_some(),
is_local,
hw_ratio: format_optional_proportion(record.hw_ratio.as_deref()).into(),
cw_ratio: format_optional_proportion(record.cw_ratio.as_deref()).into(),
pw_ratio: format_optional_proportion(record.pw_ratio.as_deref()).into(),
symmetry_order: record.symmetry_order.clone().unwrap_or_default().into(),
mirror_symmetry: record.mirror_symmetry.unwrap_or(false),
};
ui.global::<LibraryModel>().set_current_detail(detail_data);
ui.global::<TiltModel>()
.set_cached_curve_material(record.preview_material.clone().unwrap_or_default().into());
let sides = sides_from_rows(
record
.angle_settings
.iter()
.map(|a| (a.facet.as_str(), a.angle.as_str(), a.index.as_str())),
);
let labels: Vec<(&str, &str)> = record
.angle_settings
.iter()
.map(|a| (a.facet.as_str(), a.index.as_str()))
.collect();
let codes = standard_codes(&labels, &sides);
let angle_items: Vec<AngleItem> = record
.angle_settings
.iter()
.zip(sides)
.zip(codes)
.map(|((a, side), code)| AngleItem {
order_idx: a.order_index as i32,
side,
code: code.into(),
facet: a.facet.clone().into(),
angle: a.angle.trim_start_matches('-').to_string().into(),
index_val: a.index.clone().into(),
notes: a.notes.clone().into(),
second_line: a.tool_line.clone().unwrap_or_default().into(),
})
.collect();
ui.global::<LibraryModel>()
.set_current_angles(ModelRc::new(VecModel::from(angle_items.clone())));
let file_items: Vec<FileItem> = record
.attachments
.iter()
.map(|f| {
let size_kb = f.size as f64 / 1024.0;
FileItem {
name: f.name.clone().into(),
url: f.url.clone().into(),
size_str: format!("{size_kb:.1} KB").into(),
}
})
.collect();
ui.global::<LibraryModel>()
.set_current_files(ModelRc::new(VecModel::from(file_items)));
ui.global::<LibraryModel>()
.set_selected_entry_id(record.entry_id as i32);
apply_reconstructed_planes(
ui,
render_ctx,
record.entry_id,
ReconstructedPlanesInput {
shape: record.shape.as_deref(),
index_gear: record.index_gear.as_deref(),
angle_items: &angle_items,
refractive_index: record.refractive_index.as_deref(),
preview_material: record.preview_material.as_deref(),
real_design: None,
},
);
if ui.get_render_view_tab() == 0 && ui.global::<ViewportModel>().get_live_view_mode() == 0 {
let ctx = render_ctx
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
resubmit_live_solid(ui, &ctx, preview_state);
}
}
#[cfg(test)]
mod reply_is_current_tests {
use super::{LibrarySource, reply_is_current};
use crate::settings::WorkerSettings;
fn worker(address: &str) -> WorkerSettings {
WorkerSettings {
address: address.to_string(),
..WorkerSettings::default()
}
}
#[test]
fn current_seq_and_matching_remote_source_is_current() {
let source = LibrarySource::Remote(worker("10.0.0.5:9443"));
assert!(reply_is_current(true, &source, &source));
}
#[test]
fn a_superseded_seq_is_never_current_even_with_a_matching_source() {
let source = LibrarySource::Remote(worker("10.0.0.5:9443"));
assert!(!reply_is_current(false, &source, &source));
}
#[test]
fn switching_to_local_before_the_reply_lands_is_never_current() {
let request_source = LibrarySource::Remote(worker("10.0.0.5:9443"));
assert!(!reply_is_current(
true,
&request_source,
&LibrarySource::Local
));
}
#[test]
fn switching_to_a_different_remote_worker_before_the_reply_lands_is_never_current() {
let request_source = LibrarySource::Remote(worker("10.0.0.5:9443"));
let current_source = LibrarySource::Remote(worker("10.0.0.6:9443"));
assert!(!reply_is_current(true, &request_source, ¤t_source));
}
#[test]
fn a_stale_seq_and_a_moved_source_together_are_still_never_current() {
let request_source = LibrarySource::Remote(worker("10.0.0.5:9443"));
assert!(!reply_is_current(
false,
&request_source,
&LibrarySource::Local
));
}
}