use serde::{Deserialize, Serialize}; use uuid::Uuid; use yewdux::{prelude::*, storage}; use plate_tool_lib::plate::*; use plate_tool_lib::plate_instances::PlateInstance; use plate_tool_lib::transfer::Transfer; #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, Store)] #[store(storage = "session")] #[non_exhaustive] pub struct CurrentTransfer { pub transfer: Transfer, } #[derive(PartialEq, Clone, Copy, Serialize, Deserialize)] pub struct Preferences { #[serde(default)] pub in_transfer_hashes: bool, #[serde(default)] pub volume_heatmap: bool, #[serde(default)] pub csv_export_type: CsvExportType, #[serde(default)] pub show_current_coordinates: bool, } impl Default for Preferences { fn default() -> Self { Self { in_transfer_hashes: true, volume_heatmap: false, csv_export_type: CsvExportType::Normal, show_current_coordinates: false, } } } #[derive(PartialEq, Clone, Copy, Serialize, Deserialize, Default)] pub enum CsvExportType { #[default] Normal, EchoClient, } impl std::fmt::Display for CsvExportType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { CsvExportType::Normal => write!(f, "Normal"), CsvExportType::EchoClient => write!(f, "Echo Client"), } } } impl From<&str> for CsvExportType { fn from(value: &str) -> Self { match value.trim().to_lowercase().as_str() { "normal" => CsvExportType::Normal, "echo client" => CsvExportType::EchoClient, _ => CsvExportType::default(), } } } #[derive(Default, PartialEq, Clone, Serialize, Deserialize)] #[non_exhaustive] pub struct MainState { pub source_plates: Vec, pub destination_plates: Vec, pub transfers: Vec, pub selected_source_plate: Uuid, pub selected_dest_plate: Uuid, pub selected_transfer: Uuid, #[serde(default)] pub preferences: Preferences, } impl Store for MainState { fn new(context: &yewdux::Context) -> Self { init_listener( || storage::StorageListener::::new(storage::Area::Local), context, ); storage::load(storage::Area::Local) .expect("Unable to load state") .unwrap_or_default() } fn should_notify(&self, old: &Self) -> bool { self != old } } impl MainState { fn purge_transfers(&mut self) { // Removes any transfers for which the associated plates are gone self.transfers.retain(|tr| { self.source_plates .iter() .any(|spi| spi.get_uuid() == tr.source_id) && self .destination_plates .iter() .any(|dpi| dpi.get_uuid() == tr.dest_id) }); } pub fn add_source_plate(&mut self, plate: PlateInstance) { assert!(plate.plate.plate_type == PlateType::Source); self.source_plates.push(plate); } pub fn add_dest_plate(&mut self, plate: PlateInstance) { assert!(plate.plate.plate_type == PlateType::Destination); self.destination_plates.push(plate); } pub fn del_plate(&mut self, id: Uuid) { if let Some(index) = self .source_plates .iter() .position(|spi| spi.get_uuid() == id) { self.source_plates.swap_remove(index); self.purge_transfers(); } if let Some(index) = self .destination_plates .iter() .position(|dpi| dpi.get_uuid() == id) { self.destination_plates.swap_remove(index); self.purge_transfers(); } } pub fn rename_plate(&mut self, id: Uuid, new_name: &str) { if let Some(index) = self .source_plates .iter() .position(|spi| spi.get_uuid() == id) { self.source_plates[index].change_name(new_name.to_string()); } if let Some(index) = self .destination_plates .iter() .position(|dpi| dpi.get_uuid() == id) { self.destination_plates[index].change_name(new_name.to_string()); } } pub fn change_format(&mut self, id: Uuid, new_format: &PlateFormat) { if let Some(index) = self .source_plates .iter() .position(|spi| spi.get_uuid() == id) { self.source_plates[index].change_format(new_format); } if let Some(index) = self .destination_plates .iter() .position(|dpi| dpi.get_uuid() == id) { self.destination_plates[index].change_format(new_format); } } }