diff --git a/plate-tool-web/src/components/callbacks/main_window_callbacks.rs b/plate-tool-web/src/components/callbacks/main_window_callbacks.rs deleted file mode 100644 index a4a539c..0000000 --- a/plate-tool-web/src/components/callbacks/main_window_callbacks.rs +++ /dev/null @@ -1,232 +0,0 @@ -#![allow(non_snake_case)] - - -use js_sys::Array; - - -use wasm_bindgen::{prelude::*, JsCast, JsValue}; -use web_sys::{ - Blob, HtmlAnchorElement, HtmlDialogElement, HtmlElement, - HtmlFormElement, HtmlInputElement, Url, -}; -use yew::prelude::*; -use yewdux::prelude::*; - -use crate::components::states::{CurrentTransfer, MainState}; - -use crate::state_to_csv; - -type NoParamsCallback = Box; - -pub fn create_close_button(close: &Closure) -> HtmlElement { - let document = web_sys::window().unwrap().document().unwrap(); - let close_button = document - .create_element("button") - .unwrap() - .dyn_into::() - .unwrap(); - close_button.set_text_content(Some("✖")); - close_button.set_class_name("close_button"); - close_button.set_onclick(Some(close.as_ref().unchecked_ref())); - - close_button -} - -pub fn toggle_in_transfer_hashes_callback( - main_dispatch: Dispatch, -) -> Callback { - let main_dispatch = main_dispatch.clone(); - Callback::from(move |_| { - main_dispatch.reduce_mut(|state| { - state.preferences.in_transfer_hashes ^= true; - }) - }) -} - -pub fn toggle_volume_heatmap_callback( - main_dispatch: Dispatch, -) -> Callback { - let main_dispatch = main_dispatch.clone(); - Callback::from(move |_| { - main_dispatch.reduce_mut(|state| { - state.preferences.volume_heatmap ^= true; - }) - }) -} - -pub fn new_plate_dialog_callback( - new_plate_dialog_is_open: UseStateHandle, -) -> NoParamsCallback { - let new_plate_dialog_is_open = new_plate_dialog_is_open.clone(); - Box::new(move |_| { - new_plate_dialog_is_open.set(false); - }) -} - -pub fn open_new_plate_dialog_callback( - new_plate_dialog_is_open: UseStateHandle, -) -> NoParamsCallback { - let new_plate_dialog_is_open = new_plate_dialog_is_open.clone(); - Box::new(move |_| { - new_plate_dialog_is_open.set(true); - }) -} - -pub fn new_button_callback( - main_dispatch: Dispatch, - ct_dispatch: Dispatch, -) -> Callback { - Callback::from(move |_| { - let window = web_sys::window().unwrap(); - let confirm = - window.confirm_with_message("This will reset all plates and transfers. Proceed?"); - if let Ok(confirm) = confirm { - if confirm { - main_dispatch.set(MainState::default()); - ct_dispatch.set(CurrentTransfer::default()); - } - } - }) -} - -fn save_str(data: &str, name: &str) { - let blob = - Blob::new_with_str_sequence(&Array::from_iter(std::iter::once(JsValue::from_str(data)))); - if let Ok(blob) = blob { - let url = Url::create_object_url_with_blob(&blob).expect("We have a blob, why not URL?"); - // Beneath is the cool hack to download files - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let anchor = document - .create_element("a") - .unwrap() - .dyn_into::() - .unwrap(); - anchor.set_download(name); - anchor.set_href(&url); - anchor.click(); - } -} - -pub fn export_csv_button_callback(main_state: std::rc::Rc) -> Callback { - Callback::from(move |_| { - if main_state.transfers.is_empty() { - web_sys::window() - .unwrap() - .alert_with_message("No transfers to export.") - .unwrap(); - return; - } - web_sys::window().unwrap().alert_with_message("CSV export is currently not importable. Export as JSON if you'd like to back up your work!").unwrap(); - if let Ok(csv) = state_to_csv(&main_state) { - save_str(&csv, "transfers.csv"); - } - }) -} - -pub fn export_json_button_callback(main_state: std::rc::Rc) -> Callback { - Callback::from(move |_| { - if let Ok(json) = serde_json::to_string(&main_state) { - save_str(&json, "plate-tool-state.json"); - } else { - web_sys::window() - .unwrap() - .alert_with_message("Failed to export.") - .unwrap(); - } - }) -} - -pub fn input_json_input_callback( - main_dispatch: Dispatch, - modal: HtmlDialogElement, -) -> Closure { - Closure::::new(move |e: Event| { - if let Some(input) = e.current_target() { - let input = input - .dyn_into::() - .expect("We know this is an input."); - if let Some(files) = input.files() { - if let Some(file) = files.get(0) { - let fr = web_sys::FileReader::new().unwrap(); - fr.read_as_text(&file).unwrap(); - let fr1 = fr.clone(); // Clone to avoid outliving closure - let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut - // trait - let modal = modal.clone(); - let onload = Closure::::new(move |_: Event| { - if let Some(value) = &fr1.result().ok().and_then(|v| v.as_string()) { - let ms = serde_json::from_str::(value); - match ms { - Ok(ms) => main_dispatch.set(ms), - Err(e) => log::debug!("{:?}", e), - }; - modal.close(); - } - }); - fr.set_onload(Some(onload.as_ref().unchecked_ref())); - onload.forget(); // Magic (don't touch) - } - } - } - }) -} - -pub fn import_json_button_callback(main_dispatch: Dispatch) -> Callback { - Callback::from(move |_| { - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let body = document.body().unwrap(); - let modal = document - .create_element("dialog") - .unwrap() - .dyn_into::() - .unwrap(); - modal.set_text_content(Some("Import File:")); - let onclose_callback = { - let modal = modal.clone(); - Closure::::new(move |_: Event| { - modal.remove(); - }) - }; - modal.set_onclose(Some(onclose_callback.as_ref().unchecked_ref())); - - let close_button = create_close_button(&onclose_callback); - onclose_callback.forget(); - modal.append_child(&close_button).unwrap(); - - let form = document - .create_element("form") - .unwrap() - .dyn_into::() - .unwrap(); - let input = document - .create_element("input") - .unwrap() - .dyn_into::() - .unwrap(); - input.set_type("file"); - input.set_accept(".json"); - form.append_child(&input).unwrap(); - - let input_callback = { - let main_dispatch = main_dispatch.clone(); - let modal = modal.clone(); - input_json_input_callback(main_dispatch, modal) - }; - input.set_onchange(Some(input_callback.as_ref().unchecked_ref())); - input_callback.forget(); // Magic straight from the docs, don't touch :( - - modal.append_child(&form).unwrap(); - body.append_child(&modal).unwrap(); - modal.show_modal().unwrap(); - }) -} - -pub use super::import_csv_callbacks::import_transfer_csv_callback; - -pub use super::import_csv_callbacks::import_transfer_csv_submit_callback; - -pub use super::import_csv_callbacks::import_transfer_csv_onload_callback; - -pub use super::import_csv_callbacks::import_transfer_csv_input_callback; diff --git a/plate-tool-web/src/components/callbacks/main_window_callbacks/export_callbacks.rs b/plate-tool-web/src/components/callbacks/main_window_callbacks/export_callbacks.rs new file mode 100644 index 0000000..94a796c --- /dev/null +++ b/plate-tool-web/src/components/callbacks/main_window_callbacks/export_callbacks.rs @@ -0,0 +1,64 @@ +#![allow(non_snake_case)] + +use js_sys::Array; + +use wasm_bindgen::{prelude::*, JsCast, JsValue}; +use web_sys::{ + Blob, HtmlAnchorElement, HtmlDialogElement, HtmlElement, HtmlFormElement, HtmlInputElement, Url, +}; +use yew::prelude::*; +use yewdux::prelude::*; + +use crate::components::states::{CurrentTransfer, MainState}; + +use crate::state_to_csv; + +type NoParamsCallback = Box; + +pub fn export_csv_button_callback(main_state: std::rc::Rc) -> Callback { + Callback::from(move |_| { + if main_state.transfers.is_empty() { + web_sys::window() + .unwrap() + .alert_with_message("No transfers to export.") + .unwrap(); + return; + } + web_sys::window().unwrap().alert_with_message("CSV export is currently not importable. Export as JSON if you'd like to back up your work!").unwrap(); + if let Ok(csv) = state_to_csv(&main_state) { + save_str(&csv, "transfers.csv"); + } + }) +} + +pub fn export_json_button_callback(main_state: std::rc::Rc) -> Callback { + Callback::from(move |_| { + if let Ok(json) = serde_json::to_string(&main_state) { + save_str(&json, "plate-tool-state.json"); + } else { + web_sys::window() + .unwrap() + .alert_with_message("Failed to export.") + .unwrap(); + } + }) +} + +fn save_str(data: &str, name: &str) { + let blob = + Blob::new_with_str_sequence(&Array::from_iter(std::iter::once(JsValue::from_str(data)))); + if let Ok(blob) = blob { + let url = Url::create_object_url_with_blob(&blob).expect("We have a blob, why not URL?"); + // Beneath is the cool hack to download files + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); + let anchor = document + .create_element("a") + .unwrap() + .dyn_into::() + .unwrap(); + anchor.set_download(name); + anchor.set_href(&url); + anchor.click(); + } +} diff --git a/plate-tool-web/src/components/callbacks/import_csv_callbacks.rs b/plate-tool-web/src/components/callbacks/main_window_callbacks/import_csv_callbacks.rs similarity index 99% rename from plate-tool-web/src/components/callbacks/import_csv_callbacks.rs rename to plate-tool-web/src/components/callbacks/main_window_callbacks/import_csv_callbacks.rs index a7f15b8..3a37b01 100644 --- a/plate-tool-web/src/components/callbacks/import_csv_callbacks.rs +++ b/plate-tool-web/src/components/callbacks/main_window_callbacks/import_csv_callbacks.rs @@ -15,7 +15,7 @@ use plate_tool_lib::transfer_region::{Region, TransferRegion}; use plate_tool_lib::csv::{auto, string_well_to_pt, TransferRecord}; -use super::main_window_callbacks::create_close_button; +use super::create_close_button; pub fn import_transfer_csv_input_callback( main_dispatch: Dispatch, diff --git a/plate-tool-web/src/components/callbacks/main_window_callbacks/input_json_callbacks.rs b/plate-tool-web/src/components/callbacks/main_window_callbacks/input_json_callbacks.rs new file mode 100644 index 0000000..d520c7b --- /dev/null +++ b/plate-tool-web/src/components/callbacks/main_window_callbacks/input_json_callbacks.rs @@ -0,0 +1,98 @@ +#![allow(non_snake_case)] + +use wasm_bindgen::{prelude::*, JsCast}; +use web_sys::{HtmlDialogElement, HtmlFormElement, HtmlInputElement}; +use yew::prelude::*; +use yewdux::prelude::*; + +use crate::components::states::MainState; + +use super::create_close_button; + +type NoParamsCallback = Box; + +pub fn input_json_input_callback( + main_dispatch: Dispatch, + modal: HtmlDialogElement, +) -> Closure { + Closure::::new(move |e: Event| { + if let Some(input) = e.current_target() { + let input = input + .dyn_into::() + .expect("We know this is an input."); + if let Some(files) = input.files() { + if let Some(file) = files.get(0) { + let fr = web_sys::FileReader::new().unwrap(); + fr.read_as_text(&file).unwrap(); + let fr1 = fr.clone(); // Clone to avoid outliving closure + let main_dispatch = main_dispatch.clone(); // Clone to satisfy FnMut + // trait + let modal = modal.clone(); + let onload = Closure::::new(move |_: Event| { + if let Some(value) = &fr1.result().ok().and_then(|v| v.as_string()) { + let ms = serde_json::from_str::(value); + match ms { + Ok(ms) => main_dispatch.set(ms), + Err(e) => log::debug!("{:?}", e), + }; + modal.close(); + } + }); + fr.set_onload(Some(onload.as_ref().unchecked_ref())); + onload.forget(); // Magic (don't touch) + } + } + } + }) +} + +pub fn import_json_button_callback(main_dispatch: Dispatch) -> Callback { + Callback::from(move |_| { + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); + let body = document.body().unwrap(); + let modal = document + .create_element("dialog") + .unwrap() + .dyn_into::() + .unwrap(); + modal.set_text_content(Some("Import File:")); + let onclose_callback = { + let modal = modal.clone(); + Closure::::new(move |_: Event| { + modal.remove(); + }) + }; + modal.set_onclose(Some(onclose_callback.as_ref().unchecked_ref())); + + let close_button = create_close_button(&onclose_callback); + onclose_callback.forget(); + modal.append_child(&close_button).unwrap(); + + let form = document + .create_element("form") + .unwrap() + .dyn_into::() + .unwrap(); + let input = document + .create_element("input") + .unwrap() + .dyn_into::() + .unwrap(); + input.set_type("file"); + input.set_accept(".json"); + form.append_child(&input).unwrap(); + + let input_callback = { + let main_dispatch = main_dispatch.clone(); + let modal = modal.clone(); + input_json_input_callback(main_dispatch, modal) + }; + input.set_onchange(Some(input_callback.as_ref().unchecked_ref())); + input_callback.forget(); // Magic straight from the docs, don't touch :( + + modal.append_child(&form).unwrap(); + body.append_child(&modal).unwrap(); + modal.show_modal().unwrap(); + }) +} diff --git a/plate-tool-web/src/components/callbacks/main_window_callbacks/mod.rs b/plate-tool-web/src/components/callbacks/main_window_callbacks/mod.rs new file mode 100644 index 0000000..d9ec7b2 --- /dev/null +++ b/plate-tool-web/src/components/callbacks/main_window_callbacks/mod.rs @@ -0,0 +1,18 @@ +mod export_callbacks; +mod input_json_callbacks; +mod import_csv_callbacks; +mod settings_callbacks; +mod plate_edits_callbacks; +mod util; + +use util::*; + +pub use export_callbacks::*; + +pub use input_json_callbacks::*; + +pub use import_csv_callbacks::*; + +pub use settings_callbacks::*; + +pub use plate_edits_callbacks::*; diff --git a/plate-tool-web/src/components/callbacks/main_window_callbacks/plate_edits_callbacks.rs b/plate-tool-web/src/components/callbacks/main_window_callbacks/plate_edits_callbacks.rs new file mode 100644 index 0000000..2308193 --- /dev/null +++ b/plate-tool-web/src/components/callbacks/main_window_callbacks/plate_edits_callbacks.rs @@ -0,0 +1,51 @@ +#![allow(non_snake_case)] + +use js_sys::Array; + +use wasm_bindgen::{prelude::*, JsCast, JsValue}; +use web_sys::{ + Blob, HtmlAnchorElement, HtmlDialogElement, HtmlElement, HtmlFormElement, HtmlInputElement, Url, +}; +use yew::prelude::*; +use yewdux::prelude::*; + +use crate::components::states::{CurrentTransfer, MainState}; + +use crate::state_to_csv; + +type NoParamsCallback = Box; + +pub fn new_plate_dialog_callback( + new_plate_dialog_is_open: UseStateHandle, +) -> NoParamsCallback { + let new_plate_dialog_is_open = new_plate_dialog_is_open.clone(); + Box::new(move |_| { + new_plate_dialog_is_open.set(false); + }) +} + +pub fn open_new_plate_dialog_callback( + new_plate_dialog_is_open: UseStateHandle, +) -> NoParamsCallback { + let new_plate_dialog_is_open = new_plate_dialog_is_open.clone(); + Box::new(move |_| { + new_plate_dialog_is_open.set(true); + }) +} + +pub fn new_button_callback( + main_dispatch: Dispatch, + ct_dispatch: Dispatch, +) -> Callback { + Callback::from(move |_| { + let window = web_sys::window().unwrap(); + let confirm = + window.confirm_with_message("This will reset all plates and transfers. Proceed?"); + if let Ok(confirm) = confirm { + if confirm { + main_dispatch.set(MainState::default()); + ct_dispatch.set(CurrentTransfer::default()); + } + } + }) +} diff --git a/plate-tool-web/src/components/callbacks/main_window_callbacks/settings_callbacks.rs b/plate-tool-web/src/components/callbacks/main_window_callbacks/settings_callbacks.rs new file mode 100644 index 0000000..4b36a54 --- /dev/null +++ b/plate-tool-web/src/components/callbacks/main_window_callbacks/settings_callbacks.rs @@ -0,0 +1,30 @@ +#![allow(non_snake_case)] + +use yew::prelude::*; +use yewdux::prelude::*; + +use crate::components::states::MainState; + +type NoParamsCallback = Box; + +pub fn toggle_in_transfer_hashes_callback( + main_dispatch: Dispatch, +) -> Callback { + let main_dispatch = main_dispatch.clone(); + Callback::from(move |_| { + main_dispatch.reduce_mut(|state| { + state.preferences.in_transfer_hashes ^= true; + }) + }) +} + +pub fn toggle_volume_heatmap_callback( + main_dispatch: Dispatch, +) -> Callback { + let main_dispatch = main_dispatch.clone(); + Callback::from(move |_| { + main_dispatch.reduce_mut(|state| { + state.preferences.volume_heatmap ^= true; + }) + }) +} diff --git a/plate-tool-web/src/components/callbacks/main_window_callbacks/util.rs b/plate-tool-web/src/components/callbacks/main_window_callbacks/util.rs new file mode 100644 index 0000000..92d2c1f --- /dev/null +++ b/plate-tool-web/src/components/callbacks/main_window_callbacks/util.rs @@ -0,0 +1,21 @@ +#![allow(non_snake_case)] + +use wasm_bindgen::{prelude::*, JsCast}; +use web_sys::HtmlElement; +use yew::prelude::*; + +type NoParamsCallback = Box; + +pub fn create_close_button(close: &Closure) -> HtmlElement { + let document = web_sys::window().unwrap().document().unwrap(); + let close_button = document + .create_element("button") + .unwrap() + .dyn_into::() + .unwrap(); + close_button.set_text_content(Some("✖")); + close_button.set_class_name("close_button"); + close_button.set_onclick(Some(close.as_ref().unchecked_ref())); + + close_button +} diff --git a/plate-tool-web/src/components/callbacks/mod.rs b/plate-tool-web/src/components/callbacks/mod.rs index daec33c..175182a 100644 --- a/plate-tool-web/src/components/callbacks/mod.rs +++ b/plate-tool-web/src/components/callbacks/mod.rs @@ -2,4 +2,3 @@ pub mod main_window_callbacks; pub mod new_plate_dialog_callbacks; pub mod transfer_menu_callbacks; pub mod tree_callbacks; -mod import_csv_callbacks;