diff --git a/Cargo.lock b/Cargo.lock index 9f3b436d..a526d883 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1765,6 +1765,7 @@ version = "0.1.0" dependencies = [ "bincode", "chrono", + "console_error_panic_hook", "derive_enum_iter", "derive_enum_primitive", "dotenv", diff --git a/web/Cargo.toml b/web/Cargo.toml index 353fcccb..0a7a421c 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -34,6 +34,8 @@ dotenv = { version = "*" } wasm-logger = { version = "*" } log = "*" +console_error_panic_hook = { version = "*" } + [dependencies.wee_alloc] version = "*" features = ["static_array_backend"] diff --git a/web/src/components/styled_image_input.rs b/web/src/components/styled_image_input.rs index b5b1b595..6f57c93d 100644 --- a/web/src/components/styled_image_input.rs +++ b/web/src/components/styled_image_input.rs @@ -52,11 +52,9 @@ impl<'l> StyledImageInput<'l> { let v = input .files() .map(|list| { - let mut v = vec![]; - for i in 0..list.length() { - v.push(list.get(i).unwrap()); - } - v + (0..list.length()) + .filter_map(|i| list.get(i)) + .collect::>() }) .unwrap_or_default(); Msg::FileInputChanged(field_id, v) diff --git a/web/src/lib.rs b/web/src/lib.rs index 67babc64..a2ddb37a 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -34,8 +34,8 @@ mod shared; pub mod validations; mod ws; -#[global_allocator] -static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; +// #[global_allocator] +// static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; #[derive(Debug)] #[repr(C)] @@ -179,7 +179,7 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders) { WebSocketChanged::WsMsg(mut ws_msg) => { ws::update(&mut ws_msg, model, orders); orders.skip(); - Msg::WebSocketChange(WebSocketChanged::WsMsg(ws_msg)) + return; } WebSocketChanged::WebSocketMessageLoaded(v) => { match bincode::deserialize(v.as_slice()) { @@ -325,6 +325,8 @@ fn resolve_page(url: Url) -> Option { #[wasm_bindgen] pub fn render() { + console_error_panic_hook::set_once(); + let app = seed::App::start("app", init, update, view); wasm_logger::init(wasm_logger::Config::default()); @@ -402,18 +404,18 @@ fn init(url: Url, orders: &mut impl Orders) -> Model { }); { - // let sender_clone = sender.clone(); - // let id = FieldId::ProjectSettings(ProjectFieldId::Description); - // model - // .distinct_key_up - // .keyup_wih_reset(id.to_str(), 20, move |ev| { - // let sender = sender_clone.clone(); - // let key_ev = seed::to_keyboard_event(&ev); - // let target = key_ev.target().unwrap(); - // let el = seed::to_html_el(&target); - // let value = el.inner_html(); - // sender.clone()(Some(Msg::StrInputChanged(id.clone(), - // value))); }); + let sender_clone = sender.clone(); + let id = FieldId::ProjectSettings(ProjectFieldId::Description); + model + .distinct_key_up + .keyup_wih_reset(id.to_str(), 20, move |ev| { + let sender = sender_clone.clone(); + let key_ev = seed::to_keyboard_event(&ev); + let target = key_ev.target().unwrap(); + let el = seed::to_html_el(&target); + let value = el.inner_html(); + sender.clone()(Some(Msg::StrInputChanged(id.clone(), value))); + }); } open_socket(&mut model, orders); diff --git a/web/src/modals/epic_field.rs b/web/src/modals/epic_field.rs index cdacd3e3..ec81ab4d 100644 --- a/web/src/modals/epic_field.rs +++ b/web/src/modals/epic_field.rs @@ -42,7 +42,7 @@ where ) } -fn epic_select_option<'l>(epic: &'l Epic) -> StyledSelectOption<'l> { +fn epic_select_option(epic: &Epic) -> StyledSelectOption<'_> { StyledSelectOption { value: epic.id as u32, text: Some(epic.name.as_str()), diff --git a/web/src/modals/issues_edit/events.rs b/web/src/modals/issues_edit/events.rs new file mode 100644 index 00000000..3331dfb1 --- /dev/null +++ b/web/src/modals/issues_edit/events.rs @@ -0,0 +1,18 @@ +use jirs_data::IssueId; +use seed::prelude::*; + +pub type EvHandler = seed::EventHandler; + +pub fn on_click_close_modal() -> EvHandler { + mouse_ev(Ev::Click, |ev| { + ev.prevent_default(); + ev.stop_propagation(); + crate::Msg::ModalDropped + }) +} + +pub fn on_click_open_delete_confirm(issue_id: IssueId) -> EvHandler { + mouse_ev(Ev::Click, move |_| { + crate::Msg::ModalOpened(crate::ModalType::DeleteIssueConfirm(Some(issue_id))) + }) +} diff --git a/web/src/modals/issues_edit/mod.rs b/web/src/modals/issues_edit/mod.rs index bc6b2959..d4365d7e 100644 --- a/web/src/modals/issues_edit/mod.rs +++ b/web/src/modals/issues_edit/mod.rs @@ -2,6 +2,7 @@ pub use model::*; pub use update::*; pub use view::*; +mod events; mod model; mod update; mod view; diff --git a/web/src/modals/issues_edit/view.rs b/web/src/modals/issues_edit/view.rs index 56d6d3a8..a05128b2 100644 --- a/web/src/modals/issues_edit/view.rs +++ b/web/src/modals/issues_edit/view.rs @@ -19,7 +19,7 @@ use crate::components::styled_tip::styled_tip; use crate::modals::epic_field; use crate::modals::issues_edit::Model as EditIssueModal; use crate::modals::time_tracking::time_tracking_field; -use crate::model::{ModalType, Model}; +use crate::model::Model; use crate::shared::tracking_widget::tracking_link; use crate::{BuildMsg, EditIssueModalSection, FieldChange, FieldId, Msg}; @@ -95,14 +95,8 @@ fn modal_header(_model: &Model, modal: &EditIssueModal) -> Node { true, ))) }); - let close_handler = mouse_ev(Ev::Click, |ev| { - ev.prevent_default(); - ev.stop_propagation(); - Msg::ModalDropped - }); - let delete_confirmation_handler = mouse_ev(Ev::Click, move |_| { - Msg::ModalOpened(ModalType::DeleteIssueConfirm(Some(issue_id))) - }); + let close_handler = super::events::on_click_close_modal(); + let delete_confirmation_handler = super::events::on_click_open_delete_confirm(issue_id); let copy_button = StyledButton { variant: ButtonVariant::Empty, @@ -181,7 +175,7 @@ fn modal_header(_model: &Model, modal: &EditIssueModal) -> Node { } #[inline(always)] -fn type_select_option<'l>(t: IssueType, text: &'l str) -> StyledSelectOption<'l> { +fn type_select_option(t: IssueType, text: &str) -> StyledSelectOption<'_> { let name = t.to_label(); StyledSelectOption { class_list: name, @@ -220,7 +214,7 @@ fn left_modal_column(model: &Model, modal: &EditIssueModal) -> Node { } .render(); - let description = render_styled_editor(&description_state); + let description = render_styled_editor(description_state); let description_field = StyledField { input: description, ..Default::default() @@ -478,7 +472,7 @@ fn reporters_select( } #[inline(always)] -fn reporter_select_option<'l>(user: &'l User) -> StyledSelectOption<'l> { +fn reporter_select_option(user: &User) -> StyledSelectOption<'_> { StyledSelectOption { value: user.id as u32, icon: Some( @@ -535,7 +529,7 @@ fn assignees_select( } #[inline(always)] -fn assignee_select_option<'l>(user: &'l User) -> StyledSelectOption<'l> { +fn assignee_select_option(user: &User) -> StyledSelectOption<'_> { StyledSelectOption { value: user.id as u32, icon: Some( diff --git a/web/src/model.rs b/web/src/model.rs index 657736af..89144cea 100644 --- a/web/src/model.rs +++ b/web/src/model.rs @@ -265,7 +265,7 @@ pub struct Model { pub epics_by_id: HashMap, pub key_triggers: std::rc::Rc>>>, - // pub distinct_key_up: crate::shared::on_event::Distinct, + pub distinct_key_up: crate::shared::on_event::Distinct, pub show_extras: bool, } @@ -307,7 +307,7 @@ impl Model { modals_stack: vec![], modals: Modals::default(), key_triggers: std::rc::Rc::new(std::cell::RefCell::new(HashMap::with_capacity(20))), - // distinct_key_up: crate::shared::on_event::distinct(), + distinct_key_up: crate::shared::on_event::distinct(), } } diff --git a/web/src/pages/project_page/view/board.rs b/web/src/pages/project_page/view/board.rs index dc319439..990f8194 100644 --- a/web/src/pages/project_page/view/board.rs +++ b/web/src/pages/project_page/view/board.rs @@ -6,6 +6,7 @@ use crate::components::styled_avatar::*; use crate::components::styled_button::{ButtonVariant, StyledButton}; use crate::components::styled_icon::*; use crate::model::PageContent; +use crate::pages::project_page::{events, StatusIssueIds}; use crate::{match_page, Model, Msg, Page}; #[inline(always)] @@ -14,23 +15,11 @@ pub fn project_board_lists(model: &Model) -> Node { let now = chrono::Utc::now().naive_utc(); let rows = project_page.visible_issues.iter().map(|per_epic| { - let columns: Vec> = per_epic + let columns = per_epic .per_status_issues .iter() - .map(|per_status| { - let issues: Vec<&Issue> = per_status - .issue_ids - .iter() - .filter_map(|id| model.issues_by_id.get(id)) - .collect(); - project_issue_list( - model, - per_status.status_id, - &per_status.status_name, - issues.as_slice(), - ) - }) - .collect(); + .map(|per_status| project_issue_list(model, per_status)); + let epic_name = match per_epic.epic_ref.as_ref() { Some((id, name, starts_at, ends_at)) => { let id = *id; @@ -95,18 +84,18 @@ pub fn project_board_lists(model: &Model) -> Node { } #[inline(always)] -fn project_issue_list( - model: &Model, - status_id: IssueStatusId, - status_name: &str, - issues: &[&Issue], -) -> Node { - let issues: Vec> = issues +fn project_issue_list(model: &Model, per_status: &StatusIssueIds) -> Node { + let status_id = per_status.status_id; + let status_name = per_status.status_name.as_str(); + + let issues = per_status + .issue_ids .iter() - .map(|issue| ProjectIssue { model, issue }.render()) - .collect(); - let drop_handler = crate::pages::project_page::events::on_drop_issue_drop_zone(status_id); - let drag_over_handler = crate::pages::project_page::events::on_drag_over_move_issue(status_id); + .filter_map(|id| model.issues_by_id.get(id)) + .map(|issue| ProjectIssue { model, issue }.render()); + + let drop_handler = events::on_drop_issue_drop_zone(status_id); + let drag_over_handler = events::on_drag_over_move_issue(status_id); div![ C!["list"], @@ -133,7 +122,7 @@ impl<'l> ProjectIssue<'l> { PageContent::Project(project_page) => project_page.issue_drag.is_dragging(), _ => false, }; - let avatars: Vec> = self + let avatars = self .issue .user_ids .iter() @@ -146,8 +135,7 @@ impl<'l> ProjectIssue<'l> { ..StyledAvatar::default() } .render() - }) - .collect(); + }); let issue_type_icon = StyledIcon { icon: self.issue.issue_type.into(), @@ -166,13 +154,11 @@ impl<'l> ProjectIssue<'l> { .render(); let issue_id = self.issue.id; - let drag_started = crate::pages::project_page::events::on_drag_started_drag_issue(issue_id); - let drag_stopped = - crate::pages::project_page::events::on_drag_stop_issue_drag_stop(issue_id); - let drag_over_handler = - crate::pages::project_page::events::on_drag_enter_change_position(issue_id); - let drag_out = crate::pages::project_page::events::on_drag_leave_issue_drag_leave(issue_id); - let on_click = crate::pages::project_page::events::on_click_edit_issue(issue_id); + let drag_started = events::on_drag_started_drag_issue(self.issue.id); + let drag_stopped = events::on_drag_stop_issue_drag_stop(self.issue.id); + let drag_over_handler = events::on_drag_enter_change_position(self.issue.id); + let drag_out = events::on_drag_leave_issue_drag_leave(self.issue.id); + let on_click = events::on_click_edit_issue(self.issue.id); a![ drag_started, @@ -192,7 +178,7 @@ impl<'l> ProjectIssue<'l> { div![C!["issueTypeIcon"], issue_type_icon], div![C!["issuePriorityIcon"], priority_icon] ], - div![C!["assignees"], avatars,], + div![C!["assignees"], avatars], ] ] ] diff --git a/web/src/validations.rs b/web/src/validations.rs index 932b8728..4a0ac190 100644 --- a/web/src/validations.rs +++ b/web/src/validations.rs @@ -6,14 +6,14 @@ pub fn is_email(s: &str) -> bool { for c in s.chars() { match c { - '\n' | ' ' | '\t' | '\r' => return false, '@' if !has_at => { has_at = true; } - '@' if has_at => return false, '.' if has_at => { has_dot = true; } + '\n' | ' ' | '\t' | '\r' => return false, + '@' if has_at => return false, _ if has_dot => return true, _ => (), }