diff --git a/jirs-client/src/changes.rs b/jirs-client/src/changes.rs index cce41dd8..3c1851e0 100644 --- a/jirs-client/src/changes.rs +++ b/jirs-client/src/changes.rs @@ -31,7 +31,7 @@ pub enum UsersPageChange { #[derive(Clone, Debug, PartialEq)] pub enum ProjectPageChange { ResetForm, - SubmitForm, + SubmitProjectSettingsForm, // dragging ColumnDragStarted(IssueStatusId), ColumnDragStopped(IssueStatusId), @@ -41,6 +41,7 @@ pub enum ProjectPageChange { ColumnDropZone(IssueStatusId), // edit issue status name EditIssueStatusName(Option), + SubmitIssueStatusForm, } #[derive(Clone, Debug, PartialEq)] diff --git a/jirs-client/src/modal/delete_issue_status.rs b/jirs-client/src/modal/delete_issue_status.rs index 4bd5933f..cf474ce1 100644 --- a/jirs-client/src/modal/delete_issue_status.rs +++ b/jirs-client/src/modal/delete_issue_status.rs @@ -24,7 +24,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders) { model.ws.as_ref(), ); } - Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueStatusDelete(_))) => { + Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueStatusDeleted(_))) => { orders.skip().send_msg(Msg::ModalDropped); } _ => (), diff --git a/jirs-client/src/model.rs b/jirs-client/src/model.rs index 9e70a837..e28507a5 100644 --- a/jirs-client/src/model.rs +++ b/jirs-client/src/model.rs @@ -313,6 +313,12 @@ impl ProjectSettingsPage { ), } } + + pub fn reset(&mut self) { + self.edit_column_id = None; + self.name.reset(); + self.creating_issue_status = false; + } } #[derive(Debug, Default)] diff --git a/jirs-client/src/project_settings.rs b/jirs-client/src/project_settings.rs index 5b926c71..6de16407 100644 --- a/jirs-client/src/project_settings.rs +++ b/jirs-client/src/project_settings.rs @@ -24,218 +24,25 @@ use crate::{ model, FieldId, Msg, PageChanged, ProjectFieldId, ProjectPageChange, WebSocketChanged, }; +//###################################### +// VIEW +//###################################### + static TIME_TRACKING_FIBONACCI: &'static str = "Tracking employees’ time carries the risk of having them feel like they are being spied on. This is one of the most common fears that employees have when a time tracking system is implemented. No one likes to feel like they’re always being watched."; static TIME_TRACKING_HOURLY: &'static str = "Employees may feel intimidated by demands to track their time. Or they could feel that they’re constantly being watched and evaluated. And for overly ambitious managers, employee time tracking may open the doors to excessive micromanaging."; -pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders) { - if model.page != Page::ProjectSettings { - return; - } - - match msg { - Msg::WebSocketChange(ref change) => match change { - WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)) => { - send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref()); - send_ws_msg(WsMsg::IssueStatusesRequest, model.ws.as_ref()); - send_ws_msg(WsMsg::ProjectIssuesRequest, model.ws.as_ref()); - } - WebSocketChanged::WsMsg(WsMsg::ProjectLoaded(..)) => { - build_page_content(model); - } - _ => (), - }, - Msg::ChangePage(Page::ProjectSettings) => { - build_page_content(model); - if model.user.is_some() { - send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref()); - send_ws_msg(WsMsg::IssueStatusesRequest, model.ws.as_ref()); - send_ws_msg(WsMsg::ProjectIssuesRequest, model.ws.as_ref()); - } - } - _ => (), - } - - if model.user.is_none() || model.project.is_none() { - return; - } - - let page = match &mut model.page_content { - PageContent::ProjectSettings(page) => page, - _ => return error!("bad content type"), - }; - page.project_category_state.update(&msg, orders); - page.time_tracking.update(&msg); - page.name.update(&msg); - - match msg { - Msg::StrInputChanged(FieldId::ProjectSettings(ProjectFieldId::Name), text) => { - page.payload.name = Some(text); - } - Msg::StrInputChanged(FieldId::ProjectSettings(ProjectFieldId::Url), text) => { - page.payload.url = Some(text); - } - Msg::StrInputChanged(FieldId::ProjectSettings(ProjectFieldId::Description), text) => { - page.payload.description = Some(text); - } - Msg::StyledSelectChanged( - FieldId::ProjectSettings(ProjectFieldId::Category), - StyledSelectChange::Changed(value), - ) => { - let category = value.into(); - page.payload.category = Some(category); - } - Msg::ModalChanged(TabChanged( - FieldId::ProjectSettings(ProjectFieldId::Description), - mode, - )) => { - page.description_mode = mode; - } - Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::SubmitForm)) => { - send_ws_msg( - WsMsg::ProjectUpdateRequest(UpdateProjectPayload { - id: page.payload.id, - name: page.payload.name.clone(), - url: page.payload.url.clone(), - description: page.payload.description.clone(), - category: page.payload.category.clone(), - time_tracking: Some(page.time_tracking.value.into()), - }), - model.ws.as_ref(), - ); - } - Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStarted( - issue_status_id, - ))) => { - page.column_drag.drag(issue_status_id); - } - Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStopped( - _issue_status_id, - ))) => { - sync(model); - } - Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragLeave( - _issue_status_id, - ))) => page.column_drag.clear_last(), - Msg::PageChanged(PageChanged::ProjectSettings( - ProjectPageChange::ColumnExchangePosition(issue_bellow_id), - )) => exchange_position(issue_bellow_id, model), - Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDropZone( - _issue_status_id, - ))) => { - sync(model); - } - Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::EditIssueStatusName( - id, - ))) => { - if page.edit_column_id.is_some() && id.is_none() { - let old_id = page.edit_column_id.as_ref().cloned(); - let name = page.name.value.clone(); - if let Some((id, pos)) = model - .issue_statuses - .iter() - .find(|is| Some(is.id) == old_id) - .map(|is| (is.id, is.position)) - { - send_ws_msg( - WsMsg::IssueStatusUpdate(id, name.to_string(), pos), - model.ws.as_ref(), - ); - } - } - page.name.value = model - .issue_statuses - .iter() - .find_map(|is| { - if Some(is.id) == id { - Some(is.name.clone()) - } else { - None - } - }) - .unwrap_or_default(); - page.edit_column_id = id; - } - _ => (), - } -} - pub fn view(model: &model::Model) -> Node { let page = match &model.page_content { PageContent::ProjectSettings(page) => page, _ => return empty![], }; - let name = StyledTextarea::build(FieldId::ProjectSettings(ProjectFieldId::Name)) - .value(page.payload.name.as_ref().cloned().unwrap_or_default()) - .height(39) - .max_height(39) - .disable_auto_resize() - .build() - .into_node(); - let name_field = StyledField::build() - .label("Name") - .input(name) - .tip("") - .build() - .into_node(); + let name_field = name_field(page); - let url = StyledTextarea::build(FieldId::ProjectSettings(ProjectFieldId::Url)) - .height(39) - .max_height(39) - .disable_auto_resize() - .value(page.payload.url.as_ref().cloned().unwrap_or_default()) - .build() - .into_node(); - let url_field = StyledField::build() - .label("Url") - .input(url) - .tip("") - .build() - .into_node(); + let url_field = url_field(page); - let description = StyledEditor::build(FieldId::ProjectSettings(ProjectFieldId::Description)) - .text( - page.payload - .description - .as_ref() - .cloned() - .unwrap_or_default(), - ) - .update_on(Ev::Change) - .mode(page.description_mode.clone()) - .build() - .into_node(); - let description_field = StyledField::build() - .input(description) - .label("Description") - .tip("Describe the project in as much detail as you'd like.") - .build() - .into_node(); + let description_field = description_field(page); - let category = StyledSelect::build(FieldId::ProjectSettings(ProjectFieldId::Category)) - .opened(page.project_category_state.opened) - .text_filter(page.project_category_state.text_filter.as_str()) - .valid(true) - .normal() - .options( - ProjectCategory::ordered() - .into_iter() - .map(|c| c.to_child()) - .collect(), - ) - .selected(vec![page - .payload - .category - .as_ref() - .cloned() - .unwrap_or_default() - .to_child()]) - .build() - .into_node(); - let category_field = StyledField::build() - .label("Project Category") - .input(category) - .build() - .into_node(); + let category_field = category_field(page); let time_tracking = StyledCheckbox::build(FieldId::ProjectSettings(ProjectFieldId::TimeTracking)) @@ -259,6 +66,143 @@ pub fn view(model: &model::Model) -> Node { .build() .into_node(); + let columns_field = columns_section(model, page); + + let save_button = StyledButton::build() + .add_class("actionButton") + .on_click(mouse_ev(Ev::Click, |ev| { + ev.prevent_default(); + Msg::PageChanged(PageChanged::ProjectSettings( + ProjectPageChange::SubmitProjectSettingsForm, + )) + })) + .text("Save changes") + .build() + .into_node(); + + let form = StyledForm::build() + .heading("Project Details") + .on_submit(ev(Ev::Submit, |ev| { + ev.prevent_default(); + Msg::PageChanged(PageChanged::ProjectSettings( + ProjectPageChange::SubmitProjectSettingsForm, + )) + })) + .add_field(name_field) + .add_field(url_field) + .add_field(description_field) + .add_field(category_field) + .add_field(time_tracking_field) + .add_field(save_button) + .add_field(columns_field) + .build() + .into_node(); + + let project_section = vec![div![class!["formContainer"], form]]; + + inner_layout( + model, + "projectSettings", + project_section, + crate::modal::view(model), + ) +} + +/// Build project name input with styled field wrapper +fn name_field(page: &Box) -> Node { + let name = StyledTextarea::build(FieldId::ProjectSettings(ProjectFieldId::Name)) + .value(page.payload.name.as_ref().cloned().unwrap_or_default()) + .height(39) + .max_height(39) + .disable_auto_resize() + .build() + .into_node(); + StyledField::build() + .label("Name") + .input(name) + .tip("") + .build() + .into_node() +} + +/// Build project url input with styled field wrapper +fn url_field(page: &Box) -> Node { + let url = StyledTextarea::build(FieldId::ProjectSettings(ProjectFieldId::Url)) + .height(39) + .max_height(39) + .disable_auto_resize() + .value(page.payload.url.as_ref().cloned().unwrap_or_default()) + .build() + .into_node(); + StyledField::build() + .label("Url") + .input(url) + .tip("") + .build() + .into_node() +} + +/// Build project description text area with styled field wrapper +fn description_field(page: &Box) -> Node { + let description = StyledEditor::build(FieldId::ProjectSettings(ProjectFieldId::Description)) + .text( + page.payload + .description + .as_ref() + .cloned() + .unwrap_or_default(), + ) + .update_on(Ev::Change) + .mode(page.description_mode.clone()) + .build() + .into_node(); + StyledField::build() + .input(description) + .label("Description") + .tip("Describe the project in as much detail as you'd like.") + .build() + .into_node() +} + +/// Build project category dropdown with styled field wrapper +fn category_field(page: &Box) -> Node { + let category = StyledSelect::build(FieldId::ProjectSettings(ProjectFieldId::Category)) + .opened(page.project_category_state.opened) + .text_filter(page.project_category_state.text_filter.as_str()) + .valid(true) + .normal() + .options( + ProjectCategory::ordered() + .into_iter() + .map(|c| c.to_child()) + .collect(), + ) + .selected(vec![page + .payload + .category + .as_ref() + .cloned() + .unwrap_or_default() + .to_child()]) + .build() + .into_node(); + StyledField::build() + .label("Project Category") + .input(category) + .build() + .into_node() +} + +fn build_page_content(model: &mut Model) { + let project = match &model.project { + Some(project) => project, + _ => return, + }; + model.page_content = PageContent::ProjectSettings(Box::new(ProjectSettingsPage::new(project))); +} + +/// Build draggable columns preview with option to remove and add new columns +fn columns_section(model: &Model, page: &Box) -> Node { let width = 100f64 / (model.issue_statuses.len() + 1) as f64; let column_style = format!("width: calc({width}% - 10px)", width = width); let mut per_column_issue_count = HashMap::new(); @@ -281,124 +225,13 @@ pub fn view(model: &model::Model) -> Node { add_column(page, column_style.as_str()) ] ]; - let columns_field = StyledField::build() + StyledField::build() .add_class("columnsField") .input(columns_section) .label("Columns") .tip("Double-click on name to change it.") .build() - .into_node(); - - let save_button = StyledButton::build() - .add_class("actionButton") - .on_click(mouse_ev(Ev::Click, |ev| { - ev.prevent_default(); - Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::SubmitForm)) - })) - .text("Save changes") - .build() - .into_node(); - - let form = StyledForm::build() - .heading("Project Details") - .on_submit(ev(Ev::Submit, |ev| { - ev.prevent_default(); - Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::SubmitForm)) - })) - .add_field(name_field) - .add_field(url_field) - .add_field(description_field) - .add_field(category_field) - .add_field(time_tracking_field) - .add_field(save_button) - .add_field(columns_field) - .build() - .into_node(); - - let project_section = vec![div![class!["formContainer"], form]]; - - inner_layout( - model, - "projectSettings", - project_section, - crate::modal::view(model), - ) -} - -fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) { - let page = match &mut model.page_content { - PageContent::ProjectSettings(page) => page, - _ => return, - }; - if page.column_drag.dragged_or_last(bellow_id) { - return; - } - let dragged_id = match page.column_drag.dragged_id.as_ref().cloned() { - Some(id) => id, - _ => return error!("Nothing is dragged"), - }; - - let mut below = None; - let mut dragged = None; - let mut issues_statuses = vec![]; - std::mem::swap(&mut issues_statuses, &mut model.issue_statuses); - - for issue_status in issues_statuses.into_iter() { - match issue_status.id { - id if id == bellow_id => below = Some(issue_status), - id if id == dragged_id => dragged = Some(issue_status), - _ => model.issue_statuses.push(issue_status), - }; - } - - let mut below = match below { - Some(below) => below, - _ => return, - }; - let mut dragged = match dragged { - Some(issue_status) => issue_status, - _ => { - model.issue_statuses.push(below); - return; - } - }; - std::mem::swap(&mut dragged.position, &mut below.position); - - page.column_drag.mark_dirty(dragged.id); - page.column_drag.mark_dirty(below.id); - - model.issue_statuses.push(below); - model.issue_statuses.push(dragged); - model - .issue_statuses - .sort_by(|a, b| a.position.cmp(&b.position)); - page.column_drag.last_id = Some(bellow_id); -} - -fn sync(model: &mut Model) { - let page = match &mut model.page_content { - PageContent::ProjectSettings(page) => page, - _ => return error!("bad content type"), - }; - for id in page.column_drag.dirty.iter() { - let IssueStatus { name, position, .. } = - match model.issue_statuses.iter().find(|is| is.id == *id) { - Some(is) => is, - _ => continue, - }; - send_ws_msg( - WsMsg::IssueStatusUpdate(*id, name.clone(), *position), - model.ws.as_ref(), - ); - } -} - -fn build_page_content(model: &mut Model) { - let project = match &model.project { - Some(project) => project, - _ => return, - }; - model.page_content = PageContent::ProjectSettings(Box::new(ProjectSettingsPage::new(project))); + .into_node() } fn add_column(page: &ProjectSettingsPage, column_style: &str) -> Node { @@ -414,6 +247,12 @@ fn add_column(page: &ProjectSettingsPage, column_style: &str) -> Node { ProjectPageChange::EditIssueStatusName(None), )) }); + let on_submit = ev(Ev::Submit, move |ev| { + ev.prevent_default(); + Some(Msg::PageChanged(PageChanged::ProjectSettings( + ProjectPageChange::SubmitIssueStatusForm, + ))) + }); let input = StyledInput::build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName)) .state(&page.name) @@ -423,7 +262,10 @@ fn add_column(page: &ProjectSettingsPage, column_style: &str) -> Node { .build() .into_node(); - div![class!["columnPreview"], div![class!["columnName"], input]] + div![ + class!["columnPreview"], + div![class!["columnName"], form![on_submit, input]] + ] } else { let add_column = StyledIcon::build(Icon::Plus).build().into_node(); div![ @@ -534,3 +376,225 @@ fn show_column_preview( drag_out, ] } + +//####################################### +// Update +//####################################### + +pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders) { + if model.page != Page::ProjectSettings { + return; + } + + match msg { + Msg::WebSocketChange(ref change) => match change { + WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)) => { + send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref()); + send_ws_msg(WsMsg::IssueStatusesRequest, model.ws.as_ref()); + send_ws_msg(WsMsg::ProjectIssuesRequest, model.ws.as_ref()); + } + WebSocketChanged::WsMsg(WsMsg::ProjectLoaded(..)) => { + build_page_content(model); + } + WebSocketChanged::WsMsg(WsMsg::IssueStatusCreated(_)) => { + match &mut model.page_content { + PageContent::ProjectSettings(page) if Some(0) == page.edit_column_id => { + page.reset(); + } + _ => (), + }; + } + _ => (), + }, + Msg::ChangePage(Page::ProjectSettings) => { + build_page_content(model); + if model.user.is_some() { + send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref()); + send_ws_msg(WsMsg::IssueStatusesRequest, model.ws.as_ref()); + send_ws_msg(WsMsg::ProjectIssuesRequest, model.ws.as_ref()); + } + } + _ => (), + } + + if model.user.is_none() || model.project.is_none() { + return; + } + + let page = match &mut model.page_content { + PageContent::ProjectSettings(page) => page, + _ => return error!("bad content type"), + }; + page.project_category_state.update(&msg, orders); + page.time_tracking.update(&msg); + page.name.update(&msg); + + match msg { + Msg::StrInputChanged(FieldId::ProjectSettings(ProjectFieldId::Name), text) => { + page.payload.name = Some(text); + } + Msg::StrInputChanged(FieldId::ProjectSettings(ProjectFieldId::Url), text) => { + page.payload.url = Some(text); + } + Msg::StrInputChanged(FieldId::ProjectSettings(ProjectFieldId::Description), text) => { + page.payload.description = Some(text); + } + Msg::StyledSelectChanged( + FieldId::ProjectSettings(ProjectFieldId::Category), + StyledSelectChange::Changed(value), + ) => { + let category = value.into(); + page.payload.category = Some(category); + } + Msg::ModalChanged(TabChanged( + FieldId::ProjectSettings(ProjectFieldId::Description), + mode, + )) => { + page.description_mode = mode; + } + Msg::PageChanged(PageChanged::ProjectSettings( + ProjectPageChange::SubmitProjectSettingsForm, + )) => { + send_ws_msg( + WsMsg::ProjectUpdateRequest(UpdateProjectPayload { + id: page.payload.id, + name: page.payload.name.clone(), + url: page.payload.url.clone(), + description: page.payload.description.clone(), + category: page.payload.category.clone(), + time_tracking: Some(page.time_tracking.value.into()), + }), + model.ws.as_ref(), + ); + } + Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStarted( + issue_status_id, + ))) => { + page.column_drag.drag(issue_status_id); + } + Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStopped( + _issue_status_id, + ))) => { + sync(model); + } + Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragLeave( + _issue_status_id, + ))) => page.column_drag.clear_last(), + Msg::PageChanged(PageChanged::ProjectSettings( + ProjectPageChange::ColumnExchangePosition(issue_bellow_id), + )) => exchange_position(issue_bellow_id, model), + Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDropZone( + _issue_status_id, + ))) => { + sync(model); + } + Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::EditIssueStatusName( + id, + ))) => { + if page.edit_column_id.is_some() && id.is_none() { + let old_id = page.edit_column_id.as_ref().cloned(); + let name = page.name.value.clone(); + if let Some((id, pos)) = model + .issue_statuses + .iter() + .find(|is| Some(is.id) == old_id) + .map(|is| (is.id, is.position)) + { + send_ws_msg( + WsMsg::IssueStatusUpdate(id, name.to_string(), pos), + model.ws.as_ref(), + ); + } + } + page.name.value = model + .issue_statuses + .iter() + .find_map(|is| { + if Some(is.id) == id { + Some(is.name.clone()) + } else { + None + } + }) + .unwrap_or_default(); + page.edit_column_id = id; + } + Msg::PageChanged(PageChanged::ProjectSettings( + ProjectPageChange::SubmitIssueStatusForm, + )) => { + let name = page.name.value.clone(); + let position = model.issue_statuses.len(); + let ws_msg = WsMsg::IssueStatusCreate(name, position as i32); + send_ws_msg(ws_msg, model.ws.as_ref()); + } + _ => (), + } +} + +fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) { + let page = match &mut model.page_content { + PageContent::ProjectSettings(page) => page, + _ => return, + }; + if page.column_drag.dragged_or_last(bellow_id) { + return; + } + let dragged_id = match page.column_drag.dragged_id.as_ref().cloned() { + Some(id) => id, + _ => return error!("Nothing is dragged"), + }; + + let mut below = None; + let mut dragged = None; + let mut issues_statuses = vec![]; + std::mem::swap(&mut issues_statuses, &mut model.issue_statuses); + + for issue_status in issues_statuses.into_iter() { + match issue_status.id { + id if id == bellow_id => below = Some(issue_status), + id if id == dragged_id => dragged = Some(issue_status), + _ => model.issue_statuses.push(issue_status), + }; + } + + let mut below = match below { + Some(below) => below, + _ => return, + }; + let mut dragged = match dragged { + Some(issue_status) => issue_status, + _ => { + model.issue_statuses.push(below); + return; + } + }; + std::mem::swap(&mut dragged.position, &mut below.position); + + page.column_drag.mark_dirty(dragged.id); + page.column_drag.mark_dirty(below.id); + + model.issue_statuses.push(below); + model.issue_statuses.push(dragged); + model + .issue_statuses + .sort_by(|a, b| a.position.cmp(&b.position)); + page.column_drag.last_id = Some(bellow_id); +} + +fn sync(model: &mut Model) { + let page = match &mut model.page_content { + PageContent::ProjectSettings(page) => page, + _ => return error!("bad content type"), + }; + for id in page.column_drag.dirty.iter() { + let IssueStatus { name, position, .. } = + match model.issue_statuses.iter().find(|is| is.id == *id) { + Some(is) => is, + _ => continue, + }; + send_ws_msg( + WsMsg::IssueStatusUpdate(*id, name.clone(), *position), + model.ws.as_ref(), + ); + } +} diff --git a/jirs-client/src/shared/styled_input.rs b/jirs-client/src/shared/styled_input.rs index ee84616d..1bf00c3f 100644 --- a/jirs-client/src/shared/styled_input.rs +++ b/jirs-client/src/shared/styled_input.rs @@ -57,6 +57,10 @@ impl StyledInputState { _ => (), } } + + pub fn reset(&mut self) { + self.value.clear(); + } } #[derive(Debug)] diff --git a/jirs-client/src/ws/mod.rs b/jirs-client/src/ws/mod.rs index 30960656..e10b26b1 100644 --- a/jirs-client/src/ws/mod.rs +++ b/jirs-client/src/ws/mod.rs @@ -86,6 +86,18 @@ pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders) { .issue_statuses .sort_by(|a, b| a.position.cmp(&b.position)); } + WsMsg::IssueStatusDeleted(dropped_id) => { + let mut old = vec![]; + std::mem::swap(&mut model.issue_statuses, &mut old); + for is in old { + if is.id != *dropped_id { + model.issue_statuses.push(is); + } + } + model + .issue_statuses + .sort_by(|a, b| a.position.cmp(&b.position)); + } WsMsg::IssueDeleted(id) => { let mut old = vec![]; std::mem::swap(&mut model.issue_statuses, &mut old);