Start edit epic
This commit is contained in:
parent
b137b461a8
commit
82eb025359
@ -18,7 +18,7 @@ pub enum BoardPageChange {
|
||||
IssueDragStarted(EpicId),
|
||||
IssueDragStopped(EpicId),
|
||||
DragLeave(EpicId),
|
||||
ExchangePosition(EpicId),
|
||||
ChangePosition(EpicId),
|
||||
IssueDragOverStatus(IssueStatusId),
|
||||
IssueDropZone(IssueStatusId),
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use jirs_data::{
|
||||
CommentFieldId, InviteFieldId, IssueFieldId, ProjectFieldId, SignInFieldId, SignUpFieldId,
|
||||
UsersFieldId,
|
||||
CommentFieldId, EpicFieldId, InviteFieldId, IssueFieldId, ProjectFieldId, SignInFieldId,
|
||||
SignUpFieldId, UsersFieldId,
|
||||
};
|
||||
|
||||
pub type AvatarFilterActive = bool;
|
||||
@ -92,6 +92,8 @@ pub enum FieldId {
|
||||
// issue
|
||||
AddIssueModal(IssueFieldId),
|
||||
EditIssueModal(EditIssueModalSection),
|
||||
// epic
|
||||
EditEpic(EpicFieldId),
|
||||
// project boards
|
||||
TextFilterBoard,
|
||||
CopyButtonLabel,
|
||||
@ -202,6 +204,12 @@ impl std::fmt::Display for FieldId {
|
||||
UsersFieldId::Avatar => f.write_str("profile-avatar"),
|
||||
UsersFieldId::CurrentProject => f.write_str("profile-currentProject"),
|
||||
},
|
||||
FieldId::EditEpic(sub) => match sub {
|
||||
EpicFieldId::Name => f.write_str("epicEpic-name"),
|
||||
EpicFieldId::StartsAt => f.write_str("epicEpic-startsAt"),
|
||||
EpicFieldId::EndsAt => f.write_str("epicEpic-endsAt"),
|
||||
EpicFieldId::TransformInto => f.write_str("epicEpic-transformInto"),
|
||||
},
|
||||
FieldId::Rte(..) => f.write_str("rte"),
|
||||
}
|
||||
}
|
||||
|
@ -238,9 +238,11 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||
crate::modals::update(&msg, model, orders);
|
||||
|
||||
match model.page {
|
||||
Page::Project | Page::AddIssue | Page::EditIssue(..) | Page::DeleteEpic(..) => {
|
||||
pages::project_page::update(msg, model, orders)
|
||||
}
|
||||
Page::Project
|
||||
| Page::AddIssue
|
||||
| Page::EditIssue(..)
|
||||
| Page::DeleteEpic(..)
|
||||
| Page::EditEpic(..) => pages::project_page::update(msg, model, orders),
|
||||
Page::ProjectSettings => pages::project_settings_page::update(msg, model, orders),
|
||||
Page::SignIn => pages::sign_in_page::update(msg, model, orders),
|
||||
Page::SignUp => pages::sign_up_page::update(msg, model, orders),
|
||||
@ -256,9 +258,11 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||
|
||||
fn view(model: &model::Model) -> Node<Msg> {
|
||||
match model.page {
|
||||
Page::Project | Page::AddIssue | Page::DeleteEpic(..) | Page::EditIssue(..) => {
|
||||
pages::project_page::view(model)
|
||||
}
|
||||
Page::Project
|
||||
| Page::AddIssue
|
||||
| Page::EditIssue(..)
|
||||
| Page::DeleteEpic(..)
|
||||
| Page::EditEpic(..) => pages::project_page::view(model),
|
||||
Page::ProjectSettings => pages::project_settings_page::view(model),
|
||||
Page::SignIn => pages::sign_in_page::view(model),
|
||||
Page::SignUp => pages::sign_up_page::view(model),
|
||||
@ -280,10 +284,6 @@ fn resolve_page(url: Url) -> Option<Page> {
|
||||
Some(Ok(id)) => Page::EditIssue(id),
|
||||
_ => return None,
|
||||
},
|
||||
"delete-epic" => match url.path().get(1).as_ref().map(|s| s.parse::<i32>()) {
|
||||
Some(Ok(id)) => Page::DeleteEpic(id),
|
||||
_ => return None,
|
||||
},
|
||||
"profile" => Page::Profile,
|
||||
"add-issue" => Page::AddIssue,
|
||||
"project-settings" => Page::ProjectSettings,
|
||||
@ -292,6 +292,14 @@ fn resolve_page(url: Url) -> Option<Page> {
|
||||
"invite" => Page::Invite,
|
||||
"users" => Page::Users,
|
||||
"reports" => Page::Reports,
|
||||
"delete-epic" => match url.path().get(1).as_ref().map(|s| s.parse::<i32>()) {
|
||||
Some(Ok(id)) => Page::DeleteEpic(id),
|
||||
_ => return None,
|
||||
},
|
||||
"edit-epic" => match url.path().get(1).as_ref().map(|s| s.parse::<i32>()) {
|
||||
Some(Ok(id)) => Page::EditEpic(id),
|
||||
_ => return None,
|
||||
},
|
||||
_ => Page::Project,
|
||||
};
|
||||
Some(page)
|
||||
|
@ -1,30 +0,0 @@
|
||||
use {
|
||||
crate::model,
|
||||
jirs_data::{EpicId, IssueId},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
||||
pub struct Model {
|
||||
pub epic_id: EpicId,
|
||||
pub related_issues: Vec<IssueId>,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn new(issue_id: i32, model: &mut model::Model) -> Self {
|
||||
let related_issues = model
|
||||
.issues
|
||||
.iter()
|
||||
.filter_map(|issue| {
|
||||
if issue.epic_id == Some(issue_id) {
|
||||
Some(issue.id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Self {
|
||||
epic_id: issue_id,
|
||||
related_issues,
|
||||
}
|
||||
}
|
||||
}
|
20
jirs-client/src/modals/epics_delete/model.rs
Normal file
20
jirs-client/src/modals/epics_delete/model.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use {
|
||||
crate::model,
|
||||
jirs_data::{EpicId, IssueId},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
||||
pub struct Model {
|
||||
pub epic_id: EpicId,
|
||||
pub related_issues: Vec<IssueId>,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn new(epic_id: i32, model: &mut model::Model) -> Self {
|
||||
let related_issues = model.epic_issue_ids(epic_id);
|
||||
Self {
|
||||
epic_id,
|
||||
related_issues,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use {
|
||||
crate::{
|
||||
components::{styled_button::*, styled_confirm_modal::*, styled_icon::*, styled_modal::*},
|
||||
modals::epic_delete::Model,
|
||||
modals::epics_delete::Model,
|
||||
model,
|
||||
shared::ToNode,
|
||||
Msg,
|
5
jirs-client/src/modals/epics_edit/mod.rs
Normal file
5
jirs-client/src/modals/epics_edit/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
pub use {model::*, update::*, view::*};
|
||||
|
||||
mod model;
|
||||
mod update;
|
||||
mod view;
|
47
jirs-client/src/modals/epics_edit/model.rs
Normal file
47
jirs-client/src/modals/epics_edit/model.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use crate::FieldId;
|
||||
use jirs_data::EpicFieldId;
|
||||
use {
|
||||
crate::{
|
||||
components::{styled_input::*, styled_select::StyledSelectState},
|
||||
model,
|
||||
},
|
||||
jirs_data::{EpicId, IssueId},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
||||
pub struct Model {
|
||||
pub epic_id: EpicId,
|
||||
pub related_issues: Vec<IssueId>,
|
||||
pub name: StyledInputState,
|
||||
pub transform_into: StyledSelectState,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn new(epic_id: i32, model: &mut model::Model) -> Self {
|
||||
let name = model
|
||||
.epics_by_id
|
||||
.get(&epic_id)
|
||||
.map(|epic| epic.name.as_str())
|
||||
.unwrap_or_default();
|
||||
let related_issues = model
|
||||
.issues()
|
||||
.iter()
|
||||
.filter_map(|issue| {
|
||||
if issue.epic_id == Some(epic_id) {
|
||||
Some(issue.id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Self {
|
||||
epic_id,
|
||||
related_issues,
|
||||
name: StyledInputState::new(FieldId::EditEpic(EpicFieldId::Name), name),
|
||||
transform_into: StyledSelectState::new(
|
||||
FieldId::EditEpic(EpicFieldId::StartsAt),
|
||||
vec![],
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
5
jirs-client/src/modals/epics_edit/update.rs
Normal file
5
jirs-client/src/modals/epics_edit/update.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use {crate::Msg, seed::prelude::*};
|
||||
|
||||
pub fn update(_msg: &Msg, model: &mut crate::model::Model, _orders: &mut impl Orders<Msg>) {
|
||||
let _modal = crate::match_modal_mut!(model, DeleteEpic);
|
||||
}
|
30
jirs-client/src/modals/epics_edit/view.rs
Normal file
30
jirs-client/src/modals/epics_edit/view.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use {
|
||||
crate::{
|
||||
components::{styled_input::*, styled_modal::*},
|
||||
modals::epics_edit::Model,
|
||||
model,
|
||||
shared::ToNode,
|
||||
FieldId, Msg,
|
||||
},
|
||||
jirs_data::EpicFieldId,
|
||||
seed::{prelude::*, *},
|
||||
};
|
||||
|
||||
pub fn view(_model: &model::Model, modal: &Model) -> Node<Msg> {
|
||||
let transform = if modal.related_issues.is_empty() {
|
||||
Node::Empty
|
||||
} else {
|
||||
div![]
|
||||
};
|
||||
StyledModal::build()
|
||||
.center()
|
||||
.child(h1!["Edit epic"])
|
||||
.child(
|
||||
StyledInput::build()
|
||||
.build(FieldId::EditEpic(EpicFieldId::Name))
|
||||
.into_node(),
|
||||
)
|
||||
.child(transform)
|
||||
.build()
|
||||
.into_node()
|
||||
}
|
@ -2,7 +2,8 @@ pub use {epic_field::*, update::*, view::*};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub mod debug;
|
||||
pub mod epic_delete;
|
||||
pub mod epics_delete;
|
||||
pub mod epics_edit;
|
||||
pub mod issue_statuses_delete;
|
||||
pub mod issues_create;
|
||||
pub mod issues_delete;
|
||||
@ -12,3 +13,35 @@ pub mod time_tracking;
|
||||
mod epic_field;
|
||||
mod update;
|
||||
mod view;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! match_modal {
|
||||
($model: ident, $ty: ident) => {
|
||||
match $model.modals.iter().find_map(|modal| {
|
||||
if let crate::model::ModalType::$ty(modal) = modal {
|
||||
Some(modal)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some(modal) => modal,
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! match_modal_mut {
|
||||
($model: ident, $ty: ident) => {
|
||||
match $model.modals.iter_mut().find_map(|modal| {
|
||||
if let crate::model::ModalType::$ty(modal) = modal {
|
||||
Some(modal)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some(modal) => modal,
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use {
|
||||
ws::send_ws_msg,
|
||||
FieldChange, FieldId, Msg, OperationKind, ResourceKind,
|
||||
},
|
||||
jirs_data::{TimeTracking, WsMsg},
|
||||
jirs_data::{EpicId, IssueId, TimeTracking, WsMsg},
|
||||
seed::{prelude::*, *},
|
||||
};
|
||||
|
||||
@ -46,6 +46,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
Msg::ChangePage(Page::DeleteEpic(issue_id)) => {
|
||||
push_delete_epic_modal(*issue_id, model, orders)
|
||||
}
|
||||
Msg::ChangePage(Page::EditEpic(issue_id)) => push_edit_epic_modal(*issue_id, model, orders),
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
Msg::GlobalKeyDown { key, .. } if key.eq("#") => {
|
||||
@ -69,7 +70,8 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
issues_create::update(msg, model, orders);
|
||||
issues_edit::update(msg, model, orders);
|
||||
issue_statuses_delete::update(msg, model, orders);
|
||||
epic_delete::update(msg, model, orders);
|
||||
epics_edit::update(msg, model, orders);
|
||||
epics_delete::update(msg, model, orders);
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,13 +83,7 @@ fn push_add_modal(model: &mut Model, _orders: &mut impl Orders<Msg>) {
|
||||
})));
|
||||
}
|
||||
|
||||
fn push_delete_epic_modal(issue_id: i32, model: &mut Model, _orders: &mut impl Orders<Msg>) {
|
||||
use crate::modals::epic_delete::Model;
|
||||
let modal = Model::new(issue_id, model);
|
||||
model.modals.push(ModalType::DeleteEpic(Box::new(modal)));
|
||||
}
|
||||
|
||||
fn push_edit_modal(issue_id: i32, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
fn push_edit_modal(issue_id: EpicId, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
let time_tracking_type = model
|
||||
.project
|
||||
.as_ref()
|
||||
@ -113,3 +109,15 @@ fn push_edit_modal(issue_id: i32, model: &mut Model, orders: &mut impl Orders<Ms
|
||||
);
|
||||
model.modals.push(modal);
|
||||
}
|
||||
|
||||
fn push_edit_epic_modal(epic_id: EpicId, model: &mut Model, _orders: &mut impl Orders<Msg>) {
|
||||
use crate::modals::epics_edit::Model;
|
||||
let modal = Model::new(epic_id, model);
|
||||
model.modals.push(ModalType::EditEpic(Box::new(modal)));
|
||||
}
|
||||
|
||||
fn push_delete_epic_modal(issue_id: IssueId, model: &mut Model, _orders: &mut impl Orders<Msg>) {
|
||||
use crate::modals::epics_delete::Model;
|
||||
let modal = Model::new(issue_id, model);
|
||||
model.modals.push(ModalType::DeleteEpic(Box::new(modal)));
|
||||
}
|
||||
|
@ -14,6 +14,10 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
.modals
|
||||
.iter()
|
||||
.map(|modal| match modal {
|
||||
// epic
|
||||
ModalType::DeleteEpic(modal) => crate::modals::epics_delete::view(model, modal),
|
||||
ModalType::EditEpic(modal) => crate::modals::epics_edit::view(model, modal),
|
||||
// issue
|
||||
ModalType::EditIssue(issue_id, modal) => {
|
||||
if let Some(_issue) = model.issues_by_id.get(issue_id) {
|
||||
let details = issues_edit::view(model, modal.as_ref());
|
||||
@ -29,6 +33,7 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
}
|
||||
ModalType::DeleteIssueConfirm(_id) => crate::modals::issues_delete::view(model),
|
||||
ModalType::AddIssue(modal) => issues_create::view(model, modal),
|
||||
// comment
|
||||
ModalType::DeleteCommentConfirm(comment_id) => {
|
||||
let comment_id = *comment_id;
|
||||
StyledConfirmModal::build()
|
||||
@ -47,7 +52,6 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
}
|
||||
#[cfg(debug_assertions)]
|
||||
ModalType::DebugModal => crate::modals::debug::view(model),
|
||||
ModalType::DeleteEpic(modal) => crate::modals::epic_delete::view(model, modal),
|
||||
})
|
||||
.collect();
|
||||
section![id!["modals"], modals]
|
||||
|
@ -26,9 +26,13 @@ pub trait IssueModal {
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
||||
pub enum ModalType {
|
||||
// issue
|
||||
AddIssue(Box<crate::modals::issues_create::Model>),
|
||||
EditIssue(EpicId, Box<crate::modals::issues_edit::Model>),
|
||||
DeleteEpic(Box<crate::modals::epic_delete::Model>),
|
||||
// epic
|
||||
DeleteEpic(Box<crate::modals::epics_delete::Model>),
|
||||
EditEpic(Box<crate::modals::epics_edit::Model>),
|
||||
|
||||
DeleteIssueConfirm(EpicId),
|
||||
DeleteCommentConfirm(CommentId),
|
||||
TimeTracking(EpicId),
|
||||
@ -47,10 +51,15 @@ pub struct CommentForm {
|
||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||
pub enum Page {
|
||||
Project,
|
||||
EditIssue(EpicId),
|
||||
// epic
|
||||
DeleteEpic(EpicId),
|
||||
EditEpic(EpicId),
|
||||
// issue
|
||||
EditIssue(EpicId),
|
||||
AddIssue,
|
||||
// settings
|
||||
ProjectSettings,
|
||||
// auth
|
||||
SignIn,
|
||||
SignUp,
|
||||
Invite,
|
||||
@ -63,8 +72,9 @@ impl Page {
|
||||
pub fn to_path(self) -> String {
|
||||
match self {
|
||||
Page::Project => "/board".to_string(),
|
||||
Page::EditIssue(id) => format!("/issues/{id}", id = id),
|
||||
Page::DeleteEpic(id) => format!("/delete-epic/{id}", id = id),
|
||||
Page::EditEpic(id) => format!("/edit-epic/{id}", id = id),
|
||||
Page::EditIssue(id) => format!("/issues/{id}", id = id),
|
||||
Page::AddIssue => "/add-issue".to_string(),
|
||||
Page::ProjectSettings => "/project-settings".to_string(),
|
||||
Page::SignIn => "/login".to_string(),
|
||||
@ -107,6 +117,25 @@ impl Default for InvitationFormState {
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! match_page {
|
||||
($model: ident, $ty: ident) => {
|
||||
match &$model.page_content {
|
||||
PageContent::$ty(page) => page,
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! match_page_mut {
|
||||
($model: ident, $ty: ident) => {
|
||||
match &mut $model.page_content {
|
||||
PageContent::$ty(page) => page,
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PageContent {
|
||||
SignIn(Box<SignInPage>),
|
||||
@ -145,27 +174,39 @@ pub struct Model {
|
||||
pub page: Page,
|
||||
pub page_content: PageContent,
|
||||
|
||||
pub project: Option<Project>,
|
||||
|
||||
pub current_user_project: Option<UserProject>,
|
||||
|
||||
pub issues: Vec<Issue>,
|
||||
// issues
|
||||
issues: Vec<Issue>,
|
||||
pub issues_by_id: HashMap<EpicId, Issue>,
|
||||
|
||||
// users
|
||||
pub user: Option<User>,
|
||||
pub users: Vec<User>,
|
||||
pub users_by_id: HashMap<UserId, User>,
|
||||
|
||||
// comments
|
||||
pub comments: Vec<Comment>,
|
||||
pub comments_by_id: HashMap<CommentId, Comment>,
|
||||
|
||||
// issue_statuses
|
||||
pub issue_statuses: Vec<IssueStatus>,
|
||||
pub issue_statuses_by_id: HashMap<IssueStatusId, IssueStatus>,
|
||||
pub issue_statuses_by_name: HashMap<String, IssueStatus>,
|
||||
|
||||
// messages
|
||||
pub messages: Vec<Message>,
|
||||
|
||||
// user_projects
|
||||
pub user_projects: Vec<UserProject>,
|
||||
|
||||
// projects
|
||||
pub project: Option<Project>,
|
||||
pub projects: Vec<Project>,
|
||||
|
||||
// epics
|
||||
pub epics: Vec<Epic>,
|
||||
pub epics_by_id: HashMap<EpicId, Epic>,
|
||||
|
||||
pub show_extras: bool,
|
||||
}
|
||||
@ -194,6 +235,7 @@ impl Model {
|
||||
users: vec![],
|
||||
users_by_id: Default::default(),
|
||||
comments: vec![],
|
||||
comments_by_id: Default::default(),
|
||||
issue_statuses: vec![],
|
||||
issue_statuses_by_id: Default::default(),
|
||||
issue_statuses_by_name: Default::default(),
|
||||
@ -203,13 +245,52 @@ impl Model {
|
||||
epics: vec![],
|
||||
issues_by_id: Default::default(),
|
||||
show_extras: false,
|
||||
epics_by_id: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn issues(&self) -> &[Issue] {
|
||||
&self.issues
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn issues_mut(&mut self) -> &mut Vec<Issue> {
|
||||
&mut self.issues
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn issue_statuses(&self) -> &[IssueStatus] {
|
||||
&self.issue_statuses
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn epics(&self) -> &[Epic] {
|
||||
&self.epics
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn user(&self) -> &Option<User> {
|
||||
&self.user
|
||||
}
|
||||
|
||||
pub fn current_user_role(&self) -> UserRole {
|
||||
self.current_user_project
|
||||
.as_ref()
|
||||
.map(|up| up.role)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn epic_issue_ids(&self, epic_id: EpicId) -> Vec<IssueId> {
|
||||
self.issues()
|
||||
.iter()
|
||||
.filter_map(|issue| {
|
||||
if issue.epic_id == Some(epic_id) {
|
||||
Some(issue.id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,66 @@ impl ProjectPage {
|
||||
}
|
||||
self.visible_issues = map;
|
||||
}
|
||||
|
||||
pub fn visible_issues(
|
||||
page: &ProjectPage,
|
||||
epics: &[Epic],
|
||||
statuses: &[IssueStatus],
|
||||
issues: &[Issue],
|
||||
user: &Option<User>,
|
||||
) -> Vec<EpicIssuePerStatus> {
|
||||
let mut map = vec![];
|
||||
let epics = vec![None]
|
||||
.into_iter()
|
||||
.chain(epics.iter().map(|s| Some((s.id, s.name.as_str()))));
|
||||
|
||||
let statuses = statuses.iter().map(|s| (s.id, s.name.as_str()));
|
||||
|
||||
let mut issues: Vec<&Issue> = issues.iter().collect();
|
||||
if page.recently_updated_filter {
|
||||
let mut m = HashMap::new();
|
||||
let mut sorted = vec![];
|
||||
for issue in issues.into_iter() {
|
||||
sorted.push((issue.id, issue.updated_at));
|
||||
m.insert(issue.id, issue);
|
||||
}
|
||||
sorted.sort_by(|(_, a_time), (_, b_time)| a_time.cmp(b_time));
|
||||
issues = sorted
|
||||
.into_iter()
|
||||
.take(10)
|
||||
.flat_map(|(id, _)| m.remove(&id))
|
||||
.collect();
|
||||
issues.sort_by(|a, b| a.list_position.cmp(&b.list_position));
|
||||
}
|
||||
|
||||
for epic in epics {
|
||||
let mut per_epic_map = EpicIssuePerStatus {
|
||||
epic_ref: epic.map(|(id, name)| (id, name.to_string())),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
for (current_status_id, issue_status_name) in statuses.to_owned() {
|
||||
let mut per_status_map = StatusIssueIds {
|
||||
status_id: current_status_id,
|
||||
status_name: issue_status_name.to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
for issue in issues.iter() {
|
||||
if issue.epic_id == epic.map(|(id, _)| id)
|
||||
&& issue_filter_status(issue, current_status_id)
|
||||
&& issue_filter_with_avatars(issue, &page.active_avatar_filters)
|
||||
&& issue_filter_with_text(issue, page.text_filter.as_str())
|
||||
&& issue_filter_with_only_my(issue, page.only_my_filter, user)
|
||||
{
|
||||
per_status_map.issue_ids.push(issue.id);
|
||||
}
|
||||
}
|
||||
per_epic_map.per_status_issues.push(per_status_map);
|
||||
}
|
||||
map.push(per_epic_map);
|
||||
}
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -25,139 +25,114 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let project_page = match &mut model.page_content {
|
||||
PageContent::Project(project_page) => project_page,
|
||||
_ => return,
|
||||
};
|
||||
let mut rebuild_visible = false;
|
||||
{
|
||||
let project_page = crate::match_page_mut!(model, Project);
|
||||
|
||||
match msg {
|
||||
Msg::UserChanged(..)
|
||||
| Msg::ProjectChanged(Some(..))
|
||||
| Msg::ChangePage(Page::Project)
|
||||
| Msg::ChangePage(Page::AddIssue)
|
||||
| Msg::ChangePage(Page::EditIssue(..)) => {
|
||||
board_load(model, orders);
|
||||
}
|
||||
Msg::ResourceChanged(ResourceKind::Issue, OperationKind::SingleRemoved, ..) => {
|
||||
orders.skip().send_msg(Msg::ModalDropped);
|
||||
project_page.rebuild_visible(
|
||||
&model.epics,
|
||||
&model.issue_statuses,
|
||||
&model.issues,
|
||||
&model.user,
|
||||
);
|
||||
}
|
||||
Msg::ResourceChanged(
|
||||
ResourceKind::Issue
|
||||
| ResourceKind::Project
|
||||
| ResourceKind::IssueStatus
|
||||
| ResourceKind::Epic,
|
||||
..,
|
||||
) => {
|
||||
project_page.rebuild_visible(
|
||||
&model.epics,
|
||||
&model.issue_statuses,
|
||||
&model.issues,
|
||||
&model.user,
|
||||
);
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Type)),
|
||||
StyledSelectChanged::Text(text),
|
||||
) => {
|
||||
let modal = model
|
||||
.modals
|
||||
.iter_mut()
|
||||
.filter_map(|modal| match modal {
|
||||
ModalType::EditIssue(_, modal) => Some(modal),
|
||||
_ => None,
|
||||
})
|
||||
.last();
|
||||
if let Some(m) = modal {
|
||||
m.top_type_state.text_filter = text;
|
||||
match msg {
|
||||
Msg::UserChanged(..)
|
||||
| Msg::ProjectChanged(Some(..))
|
||||
| Msg::ChangePage(Page::Project)
|
||||
| Msg::ChangePage(Page::AddIssue)
|
||||
| Msg::ChangePage(Page::EditIssue(..)) => {
|
||||
board_load(model, orders);
|
||||
}
|
||||
}
|
||||
Msg::StrInputChanged(FieldId::TextFilterBoard, text) => {
|
||||
project_page.text_filter = text;
|
||||
project_page.rebuild_visible(
|
||||
&model.epics,
|
||||
&model.issue_statuses,
|
||||
&model.issues,
|
||||
&model.user,
|
||||
);
|
||||
}
|
||||
Msg::ProjectAvatarFilterChanged(user_id, active) => {
|
||||
if active {
|
||||
project_page.active_avatar_filters =
|
||||
std::mem::replace(&mut project_page.active_avatar_filters, vec![])
|
||||
.into_iter()
|
||||
.filter(|id| *id != user_id)
|
||||
.collect();
|
||||
} else {
|
||||
project_page.active_avatar_filters.push(user_id);
|
||||
Msg::ResourceChanged(ResourceKind::Issue, OperationKind::SingleRemoved, ..) => {
|
||||
orders.skip().send_msg(Msg::ModalDropped);
|
||||
rebuild_visible = true;
|
||||
}
|
||||
project_page.rebuild_visible(
|
||||
&model.epics,
|
||||
&model.issue_statuses,
|
||||
&model.issues,
|
||||
&model.user,
|
||||
);
|
||||
Msg::ResourceChanged(
|
||||
ResourceKind::Issue
|
||||
| ResourceKind::Project
|
||||
| ResourceKind::IssueStatus
|
||||
| ResourceKind::Epic,
|
||||
..,
|
||||
) => {
|
||||
rebuild_visible = true;
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Type)),
|
||||
StyledSelectChanged::Text(text),
|
||||
) => {
|
||||
let modal = model
|
||||
.modals
|
||||
.iter_mut()
|
||||
.filter_map(|modal| match modal {
|
||||
ModalType::EditIssue(_, modal) => Some(modal),
|
||||
_ => None,
|
||||
})
|
||||
.last();
|
||||
if let Some(m) = modal {
|
||||
m.top_type_state.text_filter = text;
|
||||
}
|
||||
}
|
||||
Msg::StrInputChanged(FieldId::TextFilterBoard, text) => {
|
||||
project_page.text_filter = text;
|
||||
rebuild_visible = true;
|
||||
}
|
||||
Msg::ProjectAvatarFilterChanged(user_id, active) => {
|
||||
if active {
|
||||
project_page.active_avatar_filters =
|
||||
std::mem::replace(&mut project_page.active_avatar_filters, vec![])
|
||||
.into_iter()
|
||||
.filter(|id| *id != user_id)
|
||||
.collect();
|
||||
} else {
|
||||
project_page.active_avatar_filters.push(user_id);
|
||||
}
|
||||
rebuild_visible = true;
|
||||
}
|
||||
Msg::ProjectToggleOnlyMy => {
|
||||
project_page.only_my_filter = !project_page.only_my_filter;
|
||||
rebuild_visible = true;
|
||||
}
|
||||
Msg::ProjectToggleRecentlyUpdated => {
|
||||
project_page.recently_updated_filter = !project_page.recently_updated_filter;
|
||||
rebuild_visible = true;
|
||||
}
|
||||
Msg::ProjectClearFilters => {
|
||||
project_page.active_avatar_filters = vec![];
|
||||
project_page.recently_updated_filter = false;
|
||||
project_page.only_my_filter = false;
|
||||
rebuild_visible = true;
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragStarted(issue_id))) => {
|
||||
crate::ws::issue::drag_started(issue_id, model)
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragStopped(_))) => {
|
||||
crate::ws::issue::sync(model, orders);
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::ChangePosition(
|
||||
issue_bellow_id,
|
||||
))) => crate::ws::issue::change_position(issue_bellow_id, model),
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragOverStatus(status))) => {
|
||||
crate::ws::issue::change_status(status, model)
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDropZone(_status))) => {
|
||||
crate::ws::issue::sync(model, orders)
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::DragLeave(_id))) => {
|
||||
project_page.issue_drag.clear_last();
|
||||
}
|
||||
Msg::DeleteIssue(issue_id) => {
|
||||
send_ws_msg(
|
||||
jirs_data::WsMsg::IssueDelete(issue_id),
|
||||
model.ws.as_ref(),
|
||||
orders,
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Msg::ProjectToggleOnlyMy => {
|
||||
project_page.only_my_filter = !project_page.only_my_filter;
|
||||
project_page.rebuild_visible(
|
||||
&model.epics,
|
||||
&model.issue_statuses,
|
||||
&model.issues,
|
||||
&model.user,
|
||||
);
|
||||
}
|
||||
Msg::ProjectToggleRecentlyUpdated => {
|
||||
project_page.recently_updated_filter = !project_page.recently_updated_filter;
|
||||
project_page.rebuild_visible(
|
||||
&model.epics,
|
||||
&model.issue_statuses,
|
||||
&model.issues,
|
||||
&model.user,
|
||||
);
|
||||
}
|
||||
Msg::ProjectClearFilters => {
|
||||
project_page.active_avatar_filters = vec![];
|
||||
project_page.recently_updated_filter = false;
|
||||
project_page.only_my_filter = false;
|
||||
project_page.rebuild_visible(
|
||||
&model.epics,
|
||||
&model.issue_statuses,
|
||||
&model.issues,
|
||||
&model.user,
|
||||
);
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragStarted(issue_id))) => {
|
||||
crate::ws::issue::drag_started(issue_id, model)
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragStopped(_))) => {
|
||||
crate::ws::issue::sync(model, orders);
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::ExchangePosition(
|
||||
issue_bellow_id,
|
||||
))) => crate::ws::issue::exchange_position(issue_bellow_id, model),
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragOverStatus(status))) => {
|
||||
crate::ws::issue::change_status(status, model)
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDropZone(_status))) => {
|
||||
crate::ws::issue::sync(model, orders)
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::DragLeave(_id))) => {
|
||||
project_page.issue_drag.clear_last();
|
||||
}
|
||||
Msg::DeleteIssue(issue_id) => {
|
||||
send_ws_msg(
|
||||
jirs_data::WsMsg::IssueDelete(issue_id),
|
||||
model.ws.as_ref(),
|
||||
orders,
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
if rebuild_visible {
|
||||
let visible_issues = ProjectPage::visible_issues(
|
||||
crate::match_page!(model, Project),
|
||||
model.epics(),
|
||||
model.issue_statuses(),
|
||||
model.issues(),
|
||||
model.user(),
|
||||
);
|
||||
crate::match_page_mut!(model, Project).visible_issues = visible_issues;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,15 @@ pub fn project_board_lists(model: &Model) -> Node<Msg> {
|
||||
let edit_button = StyledButton::build()
|
||||
.empty()
|
||||
.icon(Icon::EditAlt)
|
||||
.on_click(mouse_ev("click", move |ev| {
|
||||
ev.stop_propagation();
|
||||
ev.prevent_default();
|
||||
seed::Url::new()
|
||||
.add_path_part("edit-epic")
|
||||
.add_path_part(id.to_string())
|
||||
.go_and_push();
|
||||
Msg::ChangePage(Page::EditEpic(id))
|
||||
}))
|
||||
.build()
|
||||
.into_node();
|
||||
let delete_button = StyledButton::build()
|
||||
@ -159,7 +168,7 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
||||
ev.prevent_default();
|
||||
ev.stop_propagation();
|
||||
Some(Msg::PageChanged(PageChanged::Board(
|
||||
BoardPageChange::ExchangePosition(issue_id),
|
||||
BoardPageChange::ChangePosition(issue_id),
|
||||
)))
|
||||
});
|
||||
|
||||
|
@ -198,7 +198,7 @@ fn columns_section(model: &Model, page: &ProjectSettingsPage) -> Node<Msg> {
|
||||
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();
|
||||
for issue in model.issues.iter() {
|
||||
for issue in model.issues().iter() {
|
||||
*per_column_issue_count
|
||||
.entry(issue.issue_status_id)
|
||||
.or_insert(0) += 1;
|
||||
|
@ -196,7 +196,7 @@ fn issue_list(page: &ReportsPage, this_month_updated: &[&Issue]) -> Node<Msg> {
|
||||
|
||||
fn this_month_updated<'a>(model: &'a Model, page: &ReportsPage) -> Vec<&'a Issue> {
|
||||
model
|
||||
.issues
|
||||
.issues()
|
||||
.iter()
|
||||
.filter(|issue| {
|
||||
issue.updated_at.date() >= page.first_day && issue.updated_at.date() <= page.last_day
|
||||
|
@ -1,6 +1,7 @@
|
||||
use {
|
||||
crate::{
|
||||
model::{Model, PageContent},
|
||||
pages::project_page::ProjectPage,
|
||||
ws::send_ws_msg,
|
||||
Msg,
|
||||
},
|
||||
@ -16,7 +17,7 @@ pub fn drag_started(issue_id: EpicId, model: &mut Model) {
|
||||
project_page.issue_drag.drag(issue_id);
|
||||
}
|
||||
|
||||
pub fn exchange_position(below_id: EpicId, model: &mut Model) {
|
||||
pub fn change_position(below_id: EpicId, model: &mut Model) {
|
||||
let project_page = match &mut model.page_content {
|
||||
PageContent::Project(project_page) => project_page,
|
||||
_ => return,
|
||||
@ -30,7 +31,7 @@ pub fn exchange_position(below_id: EpicId, model: &mut Model) {
|
||||
}
|
||||
|
||||
let (issue_status_id, epic_id) = model
|
||||
.issues
|
||||
.issues()
|
||||
.iter()
|
||||
.find_map(|issue| {
|
||||
if issue.id == dragged_id {
|
||||
@ -42,7 +43,7 @@ pub fn exchange_position(below_id: EpicId, model: &mut Model) {
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut issues: Vec<Issue> = model
|
||||
.issues
|
||||
.issues_mut()
|
||||
.drain_filter(|issue| issue.issue_status_id == issue_status_id && issue.epic_id == epic_id)
|
||||
.collect();
|
||||
issues.sort_by(|a, b| a.list_position.cmp(&b.list_position));
|
||||
@ -66,16 +67,18 @@ pub fn exchange_position(below_id: EpicId, model: &mut Model) {
|
||||
iss.list_position = issue.list_position;
|
||||
}
|
||||
changed.push((issue.id, issue.list_position));
|
||||
model.issues.push(issue);
|
||||
model.issues_mut().push(issue);
|
||||
}
|
||||
|
||||
let visible = ProjectPage::visible_issues(
|
||||
crate::match_page!(model, Project),
|
||||
model.epics(),
|
||||
model.issue_statuses(),
|
||||
model.issues(),
|
||||
model.user(),
|
||||
);
|
||||
if let PageContent::Project(project_page) = &mut model.page_content {
|
||||
project_page.rebuild_visible(
|
||||
&model.epics,
|
||||
&model.issue_statuses,
|
||||
&model.issues,
|
||||
&model.user,
|
||||
);
|
||||
project_page.visible_issues = visible;
|
||||
for (id, _) in changed.iter() {
|
||||
project_page.issue_drag.mark_dirty(*id);
|
||||
}
|
||||
@ -107,18 +110,16 @@ pub fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
model.ws.as_ref(),
|
||||
orders,
|
||||
);
|
||||
if let PageContent::Project(project_page) = &mut model.page_content {
|
||||
project_page.issue_drag.clear()
|
||||
};
|
||||
crate::match_page_mut!(model, Project).issue_drag.clear();
|
||||
}
|
||||
|
||||
pub fn change_status(status_id: IssueStatusId, model: &mut Model) {
|
||||
let project_page = match &mut model.page_content {
|
||||
PageContent::Project(project_page) => project_page,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let dragged_id = match project_page.issue_drag.dragged_id.as_ref().cloned() {
|
||||
let dragged_id = match crate::match_page!(model, Project)
|
||||
.issue_drag
|
||||
.dragged_id
|
||||
.as_ref()
|
||||
.cloned()
|
||||
{
|
||||
Some(issue_id) => issue_id,
|
||||
_ => return error!("Nothing is dragged"),
|
||||
};
|
||||
@ -132,7 +133,7 @@ pub fn change_status(status_id: IssueStatusId, model: &mut Model) {
|
||||
}
|
||||
|
||||
let mut issues: Vec<Issue> = model
|
||||
.issues
|
||||
.issues_mut()
|
||||
.drain_filter(|issue| {
|
||||
if issue.id == dragged_id {
|
||||
issue.issue_status_id = status_id;
|
||||
@ -143,6 +144,7 @@ pub fn change_status(status_id: IssueStatusId, model: &mut Model) {
|
||||
|
||||
issues.sort_by(|a, b| a.list_position.cmp(&b.list_position));
|
||||
|
||||
let mut dirty = vec![];
|
||||
for mut issue in issues {
|
||||
if issue.id == dragged_id {
|
||||
issue.issue_status_id = status_id;
|
||||
@ -150,14 +152,24 @@ pub fn change_status(status_id: IssueStatusId, model: &mut Model) {
|
||||
iss.issue_status_id = status_id;
|
||||
}
|
||||
}
|
||||
project_page.issue_drag.mark_dirty(issue.id);
|
||||
model.issues.push(issue);
|
||||
|
||||
dirty.push(issue.id);
|
||||
model.issues_mut().push(issue);
|
||||
}
|
||||
{
|
||||
let project_page = crate::match_page_mut!(model, Project);
|
||||
for id in dirty {
|
||||
project_page.issue_drag.mark_dirty(id);
|
||||
}
|
||||
}
|
||||
|
||||
project_page.rebuild_visible(
|
||||
&model.epics,
|
||||
&model.issue_statuses,
|
||||
&model.issues,
|
||||
&model.user,
|
||||
let visible = ProjectPage::visible_issues(
|
||||
crate::match_page!(model, Project),
|
||||
model.epics(),
|
||||
model.issue_statuses(),
|
||||
model.issues(),
|
||||
model.user(),
|
||||
);
|
||||
|
||||
crate::match_page_mut!(model, Project).visible_issues = visible;
|
||||
}
|
||||
|
@ -219,9 +219,11 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
// issues
|
||||
WsMsg::ProjectIssuesLoaded(mut v) => {
|
||||
v.sort_by(|a, b| (a.list_position as i64).cmp(&(b.list_position as i64)));
|
||||
model.issues = v;
|
||||
{
|
||||
let _ = std::mem::replace(model.issues_mut(), v.clone());
|
||||
};
|
||||
model.issues_by_id.clear();
|
||||
for issue in model.issues.iter() {
|
||||
for issue in v {
|
||||
model.issues_by_id.insert(issue.id, issue.clone());
|
||||
}
|
||||
|
||||
@ -231,12 +233,12 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
None,
|
||||
));
|
||||
}
|
||||
WsMsg::IssueUpdated(mut issue) => {
|
||||
WsMsg::IssueUpdated(issue) => {
|
||||
let id = issue.id;
|
||||
model.issues_by_id.remove(&id);
|
||||
model.issues_by_id.insert(id, issue.clone());
|
||||
if let Some(idx) = model.issues.iter().position(|i| i.id == issue.id) {
|
||||
std::mem::swap(&mut model.issues[idx], &mut issue);
|
||||
if let Some(idx) = model.issues().iter().position(|i| i.id == issue.id) {
|
||||
let _ = std::mem::replace(&mut model.issues_mut()[idx], issue);
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Issue,
|
||||
@ -246,12 +248,12 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
WsMsg::IssueDeleted(id, _count) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut model.issues, &mut old);
|
||||
std::mem::swap(model.issues_mut(), &mut old);
|
||||
for is in old {
|
||||
if is.id == id {
|
||||
continue;
|
||||
}
|
||||
model.issues.push(is);
|
||||
model.issues_mut().push(is);
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Issue,
|
||||
@ -282,27 +284,33 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
return;
|
||||
}
|
||||
comments.sort_by(|a, b| a.updated_at.cmp(&b.updated_at));
|
||||
model.comments = comments;
|
||||
model.comments = comments.clone();
|
||||
for comment in comments {
|
||||
model.comments_by_id.insert(comment.id, comment);
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Comment,
|
||||
OperationKind::ListLoaded,
|
||||
None,
|
||||
));
|
||||
}
|
||||
WsMsg::CommentUpdated(mut comment) => {
|
||||
WsMsg::CommentUpdated(comment) => {
|
||||
let comment_id = comment.id;
|
||||
if let Some(idx) = model.comments.iter().position(|c| c.id == comment.id) {
|
||||
std::mem::swap(&mut model.comments[idx], &mut comment);
|
||||
let _ = std::mem::replace(&mut model.comments[idx], comment.clone());
|
||||
model.comments_by_id.insert(comment.id, comment);
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Comment,
|
||||
OperationKind::SingleModified,
|
||||
Some(comment.id),
|
||||
Some(comment_id),
|
||||
));
|
||||
}
|
||||
WsMsg::CommentDeleted(comment_id, _count) => {
|
||||
if let Some(idx) = model.comments.iter().position(|c| c.id == comment_id) {
|
||||
model.comments.remove(idx);
|
||||
}
|
||||
model.comments_by_id.remove(&comment_id);
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Comment,
|
||||
OperationKind::SingleRemoved,
|
||||
@ -317,7 +325,7 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
if let Some(me) = model.user.as_mut() {
|
||||
if me.id == user_id {
|
||||
me.avatar_url = Some(avatar_url.clone());
|
||||
me.avatar_url = Some(avatar_url);
|
||||
}
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
@ -361,7 +369,10 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
|
||||
// epics
|
||||
WsMsg::EpicsLoaded(epics) => {
|
||||
model.epics = epics;
|
||||
model.epics = epics.clone();
|
||||
for epic in epics {
|
||||
model.epics_by_id.insert(epic.id, epic);
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
OperationKind::ListLoaded,
|
||||
@ -370,29 +381,33 @@ pub fn update(msg: WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
WsMsg::EpicCreated(epic) => {
|
||||
let id = epic.id;
|
||||
model.epics.push(epic);
|
||||
model.epics.push(epic.clone());
|
||||
model.epics.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
model.epics_by_id.insert(epic.id, epic);
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
OperationKind::SingleCreated,
|
||||
Some(id),
|
||||
));
|
||||
}
|
||||
WsMsg::EpicUpdated(mut epic) => {
|
||||
WsMsg::EpicUpdated(epic) => {
|
||||
let epic_id = epic.id;
|
||||
if let Some(idx) = model.epics.iter().position(|e| e.id == epic.id) {
|
||||
std::mem::swap(&mut model.epics[idx], &mut epic);
|
||||
let _ = std::mem::replace(&mut model.epics[idx], epic.clone());
|
||||
}
|
||||
model.epics_by_id.insert(epic.id, epic);
|
||||
model.epics.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
OperationKind::SingleModified,
|
||||
Some(epic.id),
|
||||
Some(epic_id),
|
||||
));
|
||||
}
|
||||
WsMsg::EpicDeleted(id, _count) => {
|
||||
if let Some(idx) = model.epics.iter().position(|e| e.id == id) {
|
||||
model.epics.remove(idx);
|
||||
}
|
||||
model.epics_by_id.remove(&id);
|
||||
model.epics.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
|
@ -65,4 +65,5 @@ pub enum EpicFieldId {
|
||||
Name,
|
||||
StartsAt,
|
||||
EndsAt,
|
||||
TransformInto,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user