diff --git a/plate-tool-eframe/Cargo.toml b/plate-tool-eframe/Cargo.toml index dc66593..e71cc9a 100644 --- a/plate-tool-eframe/Cargo.toml +++ b/plate-tool-eframe/Cargo.toml @@ -11,6 +11,7 @@ eframe = { version = "0.33", default-features = false, features = [ "wayland", "persistence", ]} +poll-promise = "0.3" log = "0.4" serde = { version = "1.0", features = ["derive"] } diff --git a/plate-tool-eframe/src/app.rs b/plate-tool-eframe/src/app.rs index d1044b9..0ed0d41 100644 --- a/plate-tool-eframe/src/app.rs +++ b/plate-tool-eframe/src/app.rs @@ -23,6 +23,10 @@ pub struct MainWindowState { pub plate_display_options: PlateDisplayOptions, pub csv_export_type: CsvExportType, pub show_plates_horizontal: bool, + #[serde(skip)] + pub json_upload_promise: Option, + #[serde(skip)] + pub csv_upload_promise: Option, } impl Default for MainWindowState { @@ -32,6 +36,8 @@ impl Default for MainWindowState { plate_display_options: PlateDisplayOptions::default(), csv_export_type: CsvExportType::default(), show_plates_horizontal: false, + json_upload_promise: None, + csv_upload_promise: None, } } } @@ -53,6 +59,14 @@ impl std::fmt::Display for CsvExportType { } } +pub struct FileUploadPromise(pub poll_promise::Promise>>); + +impl std::fmt::Debug for FileUploadPromise { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("FileUploadPromise").finish() + } +} + #[non_exhaustive] #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct PlateToolEframe { @@ -276,5 +290,51 @@ impl eframe::App for PlateToolEframe { } _ => (), } + + process_promises( + &mut self.main_state, + &mut self.modal_state, + &mut self.current_transfer_state, + &mut self.main_window_state, + ); + } +} + +fn process_promises( + main_state: &mut MainState, + _modal_state: &mut ModalState, + _current_transfer_state: &mut CurrentTransferState, + main_window_state: &mut MainWindowState, +) { + if let Some(promise) = &main_window_state.json_upload_promise { + if let Some(result) = promise.0.ready() { + if let Some(data) = result { + match serde_json::from_slice::(data) { + Ok(new_main_state) => { + log::info!("Loaded new main state from file."); + *main_state = new_main_state + } + Err(e) => log::error!("Failed loading imported state JSON: {}", e), + } + } + main_window_state.json_upload_promise = None; + } + } + + if let Some(promise) = &main_window_state.csv_upload_promise { + if let Some(result) = promise.0.ready() { + log::info!("csv file read"); + if let Some(data) = result { + if let Ok(data) = String::from_utf8(data.to_vec()) { + let auto_output = plate_tool_lib::csv::read_csv_auto(&data); + main_state.source_plates.extend(auto_output.sources); + main_state + .destination_plates + .extend(auto_output.destinations); + main_state.transfers.extend(auto_output.transfers); + } + } + main_window_state.csv_upload_promise = None; + } } } diff --git a/plate-tool-eframe/src/extra_widgets.rs b/plate-tool-eframe/src/extra_widgets.rs index 4187d41..c2a4299 100644 --- a/plate-tool-eframe/src/extra_widgets.rs +++ b/plate-tool-eframe/src/extra_widgets.rs @@ -30,6 +30,7 @@ where response } +// literally why is this not given in the emath::Numeric definition fn clamp_partial(val: T, min: T, max: T) -> T { if val < min { min } else if val > max { max } diff --git a/plate-tool-eframe/src/file_handling.rs b/plate-tool-eframe/src/file_handling.rs index f6e71f5..47d90d7 100644 --- a/plate-tool-eframe/src/file_handling.rs +++ b/plate-tool-eframe/src/file_handling.rs @@ -1,3 +1,6 @@ +#[cfg(not(target_arch = "wasm32"))] +use crate::app; + // Native case, use rfd #[cfg(not(target_arch = "wasm32"))] pub fn save_file(data: &[u8], default_filename: Option<&str>) { @@ -20,27 +23,51 @@ pub fn save_file(data: &[u8], default_filename: Option<&str>) { }); } +#[cfg(not(target_arch = "wasm32"))] +pub fn upload_file(filetype: &str, extension: &str) -> app::FileUploadPromise { + use poll_promise; + use rfd::FileDialog; + + let filetype: String = filetype.to_owned(); + let extension: String = extension.to_owned(); + + app::FileUploadPromise(poll_promise::Promise::spawn_thread( + "file_upload", + move || { + FileDialog::new() + .add_filter(filetype, &[extension]) + .pick_file() + .and_then(|path| { + let data = std::fs::read(path).ok()?; + log::info!("Read {} bytes", data.len()); + Some(data) + }) + }, + )) +} + // Web case, use web_sys // Not tested yet!! #[cfg(target_arch = "wasm32")] pub fn save_file(default_filename: Option<&str>, data: &[u8]) { use wasm_bindgen::JsCast; use web_sys::{Blob, HtmlAnchorElement, Url}; - + let array = js_sys::Uint8Array::from(data); - let blob = Blob::new_with_u8_array_sequence( - &js_sys::Array::from_iter([array]) - ).unwrap(); - + let blob = Blob::new_with_u8_array_sequence(&js_sys::Array::from_iter([array])).unwrap(); + let url = Url::create_object_url_with_blob(&blob).unwrap(); - + let document = web_sys::window().unwrap().document().unwrap(); - let anchor = document.create_element("a").unwrap() - .dyn_into::().unwrap(); - + let anchor = document + .create_element("a") + .unwrap() + .dyn_into::() + .unwrap(); + anchor.set_href(&url); anchor.set_download(default_filename.unwrap_or("download.csv")); anchor.click(); - + Url::revoke_object_url(&url).unwrap(); } diff --git a/plate-tool-eframe/src/upper_menu.rs b/plate-tool-eframe/src/upper_menu.rs index 1086352..5ce917d 100644 --- a/plate-tool-eframe/src/upper_menu.rs +++ b/plate-tool-eframe/src/upper_menu.rs @@ -1,6 +1,6 @@ use crate::app::{CsvExportType, MainWindowState}; -use crate::file_handling::save_file; -use crate::main_state::MainState; +use crate::file_handling::{self, save_file}; +use crate::main_state::{self, MainState}; use crate::modals::ModalState; use crate::transfer_menu::CurrentTransferState; @@ -100,11 +100,11 @@ fn render_export_menu( .iter() .find(|dpi| dpi.get_uuid() == transfer.dest_id)?; - if main_window_state.csv_export_type != CsvExportType::EchoClient - || main_state + if main_window_state.csv_export_type == CsvExportType::EchoClient + && main_state .get_current_source_uuid() .is_some_and(|x| x != src_barcode.get_uuid()) - || main_state + && main_state .get_current_destination_uuid() .is_some_and(|x| x != dest_barcode.get_uuid()) { @@ -119,6 +119,7 @@ fn render_export_menu( }) .flatten() .collect(); + log::info!("There are {} TransferRecords to write", records.len()); let data = match main_window_state.csv_export_type { CsvExportType::Normal => records_to_csv(records), @@ -126,6 +127,7 @@ fn render_export_menu( }; if let Ok(data) = data { let bytes: &[u8] = data.as_bytes(); + log::info!("There are {} bytes to be written to chosen file", bytes.len()); save_file(bytes, Some("transfers.csv")); } } @@ -140,13 +142,47 @@ fn render_export_menu( fn render_import_menu( ui: &mut egui::Ui, - _main_state: &mut MainState, + main_state: &mut MainState, _modal_state: &mut ModalState, _current_transfer_state: &mut CurrentTransferState, - _main_window_state: &mut MainWindowState, + main_window_state: &mut MainWindowState, ) { - if ui.button("Import from JSON").clicked() {} - if ui.button("Import transfer from CSV").clicked() {} + if ui.button("Import from JSON").clicked() { + main_window_state.json_upload_promise = Some(file_handling::upload_file("JSON", "json")); + } + + if let Some(promise) = &main_window_state.json_upload_promise { + if let Some(result) = promise.0.ready() { + if let Some(data) = result { + match serde_json::from_slice::(data) { + Ok(new_main_state) => { + log::info!("Loaded new main state from file."); + *main_state = new_main_state + }, + Err(e) => log::error!("Failed loading imported state JSON: {}", e), + } + } + main_window_state.json_upload_promise = None; + } + } + + if ui.button("Import transfer from CSV").clicked() { + main_window_state.csv_upload_promise = Some(file_handling::upload_file("CSV", "csv")); + } + + if let Some(promise) = &main_window_state.csv_upload_promise { + if let Some(result) = promise.0.ready() { + if let Some(data) = result { + if let Ok(data) = String::from_utf8(data.to_vec()) { + let auto_output = plate_tool_lib::csv::read_csv_auto(&data); + main_state.source_plates.extend(auto_output.sources); + main_state.destination_plates.extend(auto_output.destinations); + main_state.transfers.extend(auto_output.transfers); + } + } + main_window_state.json_upload_promise = None; + } + } } fn render_options_menu( @@ -186,6 +222,9 @@ fn render_options_menu( }); ui.menu_button("Windows", |ui| { ui.toggle_value(&mut main_window_state.show_side_panel, "Toggle side panel"); - ui.toggle_value(&mut main_window_state.show_plates_horizontal, "Show plates horizontally"); + ui.toggle_value( + &mut main_window_state.show_plates_horizontal, + "Show plates horizontally", + ); }); }