Reduce issues memory usage
This commit is contained in:
parent
fd4e04647e
commit
5abcfb99a6
@ -2,11 +2,8 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use database_actor::issue_assignees::LoadAssignees;
|
use database_actor::issue_assignees::LoadAssignees;
|
||||||
use database_actor::issues::{LoadProjectIssues, UpdateIssue};
|
use database_actor::issues::{LoadProjectIssues, UpdateIssue};
|
||||||
use jirs_data::msg::{WsMsgIssue, WsMsgProject};
|
use jirs_data::msg::{IssueSync, WsMsgIssue, WsMsgProject};
|
||||||
use jirs_data::{
|
use jirs_data::{CreateIssuePayload, IssueAssignee, IssueFieldId, IssueId, PayloadVariant, WsMsg};
|
||||||
CreateIssuePayload, IssueAssignee, IssueFieldId, IssueId, IssueStatusId, ListPosition,
|
|
||||||
PayloadVariant, WsMsg,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{db_or_debug_and_return, AsyncHandler, WebSocketActor, WsResult};
|
use crate::{db_or_debug_and_return, AsyncHandler, WebSocketActor, WsResult};
|
||||||
|
|
||||||
@ -24,14 +21,12 @@ impl AsyncHandler<WsMsgIssue> for WebSocketActor {
|
|||||||
}
|
}
|
||||||
WsMsgIssue::IssueCreate(payload) => self.exec(payload).await,
|
WsMsgIssue::IssueCreate(payload) => self.exec(payload).await,
|
||||||
WsMsgIssue::IssueDelete(id) => self.exec(DeleteIssue { id }).await,
|
WsMsgIssue::IssueDelete(id) => self.exec(DeleteIssue { id }).await,
|
||||||
WsMsgIssue::IssueSyncListPosition(sync) => {
|
WsMsgIssue::IssueSyncListPosition(sync) => self.exec(SyncIssueListPosition(sync)).await,
|
||||||
self.exec(SyncIssueListPosition(sync)).await?;
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
WsMsgIssue::IssueUpdated(_) => Ok(None),
|
WsMsgIssue::IssueUpdated(_) => Ok(None),
|
||||||
WsMsgIssue::IssueDeleted(_, _) => Ok(None),
|
WsMsgIssue::IssueDeleted(_, _) => Ok(None),
|
||||||
WsMsgIssue::IssueCreated(_) => Ok(None),
|
WsMsgIssue::IssueCreated(_) => Ok(None),
|
||||||
|
WsMsgIssue::IssueSyncedListPosition(_) => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,27 +191,37 @@ impl AsyncHandler<LoadIssues> for WebSocketActor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SyncIssueListPosition(pub Vec<(IssueId, ListPosition, IssueStatusId, Option<IssueId>)>);
|
pub struct SyncIssueListPosition(pub Vec<IssueSync>);
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl AsyncHandler<SyncIssueListPosition> for WebSocketActor {
|
impl AsyncHandler<SyncIssueListPosition> for WebSocketActor {
|
||||||
async fn exec(&mut self, msg: SyncIssueListPosition) -> WsResult {
|
async fn exec(&mut self, msg: SyncIssueListPosition) -> WsResult {
|
||||||
let _project_id = self.require_user_project()?.project_id;
|
let _project_id = self.require_user_project()?.project_id;
|
||||||
for (issue_id, list_position, status_id, epic_id) in msg.0 {
|
let mut result = Vec::with_capacity(msg.0.len());
|
||||||
|
for issue_sync in msg.0 {
|
||||||
crate::actor_or_debug_and_ignore!(
|
crate::actor_or_debug_and_ignore!(
|
||||||
self,
|
self,
|
||||||
db,
|
db,
|
||||||
database_actor::issues::UpdateIssue {
|
database_actor::issues::UpdateIssue {
|
||||||
issue_id,
|
issue_id: issue_sync.id,
|
||||||
list_position: Some(list_position),
|
list_position: Some(issue_sync.list_position),
|
||||||
issue_status_id: Some(status_id),
|
issue_status_id: Some(issue_sync.issue_status_id),
|
||||||
epic_id: Some(epic_id),
|
epic_id: Some(issue_sync.epic_id),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|_| {}; async
|
|issue: database_actor::models::Issue| {
|
||||||
|
result.push(IssueSync {
|
||||||
|
id: issue.id,
|
||||||
|
list_position: issue.list_position,
|
||||||
|
issue_status_id: issue.issue_status_id,
|
||||||
|
epic_id: issue.epic_id,
|
||||||
|
});
|
||||||
|
}; async
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.exec(LoadIssues).await
|
Ok(Some(WsMsg::Issue(WsMsgIssue::IssueSyncedListPosition(
|
||||||
|
result,
|
||||||
|
))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,6 +119,14 @@ impl WsError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||||
|
pub struct IssueSync {
|
||||||
|
pub id: IssueId,
|
||||||
|
pub list_position: ListPosition,
|
||||||
|
pub issue_status_id: IssueStatusId,
|
||||||
|
pub epic_id: Option<IssueId>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||||
pub enum WsMsgIssue {
|
pub enum WsMsgIssue {
|
||||||
IssueUpdate(IssueId, IssueFieldId, PayloadVariant),
|
IssueUpdate(IssueId, IssueFieldId, PayloadVariant),
|
||||||
@ -127,7 +135,8 @@ pub enum WsMsgIssue {
|
|||||||
IssueDeleted(IssueId, NumberOfDeleted),
|
IssueDeleted(IssueId, NumberOfDeleted),
|
||||||
IssueCreate(CreateIssuePayload),
|
IssueCreate(CreateIssuePayload),
|
||||||
IssueCreated(Issue),
|
IssueCreated(Issue),
|
||||||
IssueSyncListPosition(Vec<(IssueId, ListPosition, IssueStatusId, Option<IssueId>)>),
|
IssueSyncListPosition(Vec<IssueSync>),
|
||||||
|
IssueSyncedListPosition(Vec<IssueSync>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<WsMsgIssue> for WsMsg {
|
impl From<WsMsgIssue> for WsMsg {
|
||||||
|
@ -169,6 +169,12 @@ macro_rules! match_page {
|
|||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
($model: ident, $ty: ident, $def: expr) => {
|
||||||
|
match &$model.page_content {
|
||||||
|
crate::model::PageContent::$ty(page) => page,
|
||||||
|
_ => return $def,
|
||||||
|
}
|
||||||
|
};
|
||||||
($model: ident, $ty: ident; Empty) => {
|
($model: ident, $ty: ident; Empty) => {
|
||||||
match &$model.page_content {
|
match &$model.page_content {
|
||||||
crate::model::PageContent::$ty(page) => page,
|
crate::model::PageContent::$ty(page) => page,
|
||||||
@ -184,6 +190,12 @@ macro_rules! match_page_mut {
|
|||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
($model: ident, $ty: ident, $def: expr) => {
|
||||||
|
match &mut $model.page_content {
|
||||||
|
PageContent::$ty(page) => page,
|
||||||
|
_ => return $def,
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -288,7 +300,7 @@ impl Model {
|
|||||||
current_user_project: None,
|
current_user_project: None,
|
||||||
about_tooltip_visible: false,
|
about_tooltip_visible: false,
|
||||||
messages_tooltip_visible: false,
|
messages_tooltip_visible: false,
|
||||||
issues: vec![],
|
issue_ids: vec![],
|
||||||
user_ids: vec![],
|
user_ids: vec![],
|
||||||
users_by_id: HashMap::with_capacity(1_000),
|
users_by_id: HashMap::with_capacity(1_000),
|
||||||
user_settings: None,
|
user_settings: None,
|
||||||
@ -354,15 +366,15 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn epic_issue_ids(&self, epic_id: EpicId) -> Vec<IssueId> {
|
pub fn epic_issue_ids(&self, epic_id: EpicId) -> Vec<IssueId> {
|
||||||
self.issues()
|
self.issue_ids
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|issue| {
|
.filter(|id| {
|
||||||
if issue.epic_id == Some(epic_id) {
|
self.issues_by_id
|
||||||
Some(issue.id)
|
.get(id)
|
||||||
} else {
|
.filter(|issue| issue.epic_id == Some(epic_id))
|
||||||
None
|
.is_some()
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
.copied()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ impl ProjectPage {
|
|||||||
where
|
where
|
||||||
IssueStream: std::iter::Iterator<Item = &'issue Issue>,
|
IssueStream: std::iter::Iterator<Item = &'issue Issue>,
|
||||||
{
|
{
|
||||||
|
let num_of_epics = epics.len();
|
||||||
let epics = vec![None].into_iter().chain(
|
let epics = vec![None].into_iter().chain(
|
||||||
epics
|
epics
|
||||||
.iter()
|
.iter()
|
||||||
@ -51,7 +52,7 @@ impl ProjectPage {
|
|||||||
&& issue_filter_with_text(issue, page.text_filter.as_str())
|
&& issue_filter_with_text(issue, page.text_filter.as_str())
|
||||||
&& issue_filter_with_only_my(issue, page.only_my_filter, user)
|
&& issue_filter_with_only_my(issue, page.only_my_filter, user)
|
||||||
});
|
});
|
||||||
let issues = if page.recently_updated_filter {
|
let mut issues = if page.recently_updated_filter {
|
||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
let mut sorted: Vec<(IssueId, NaiveDateTime)> = issues
|
let mut sorted: Vec<(IssueId, NaiveDateTime)> = issues
|
||||||
.map(|issue| {
|
.map(|issue| {
|
||||||
@ -71,12 +72,14 @@ impl ProjectPage {
|
|||||||
issues.collect()
|
issues.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
issues.sort_by(|a, b| a.list_position.cmp(&b.list_position));
|
||||||
let issues_per_epic_id = {
|
let issues_per_epic_id = {
|
||||||
let issues_len = issues.len();
|
|
||||||
issues
|
issues
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(HashMap::with_capacity(issues_len), |mut m, issue| {
|
.fold(HashMap::with_capacity(num_of_epics), |mut m, issue| {
|
||||||
m.entry(issue.epic_id).or_insert_with(Vec::new).push(issue);
|
m.entry(issue.epic_id)
|
||||||
|
.or_insert_with(|| Vec::with_capacity(100))
|
||||||
|
.push(issue);
|
||||||
m
|
m
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@ use seed::prelude::Orders;
|
|||||||
use crate::components::styled_select::StyledSelectChanged;
|
use crate::components::styled_select::StyledSelectChanged;
|
||||||
use crate::model::{Model, Page, PageContent};
|
use crate::model::{Model, Page, PageContent};
|
||||||
use crate::pages::project_page::model::ProjectPage;
|
use crate::pages::project_page::model::ProjectPage;
|
||||||
|
use crate::ws::issue::change_visible;
|
||||||
use crate::ws::{board_load, send_ws_msg};
|
use crate::ws::{board_load, send_ws_msg};
|
||||||
use crate::{
|
use crate::{
|
||||||
BoardPageChange, EditIssueModalSection, FieldId, Msg, OperationKind, PageChanged, ResourceKind,
|
BoardPageChange, EditIssueModalSection, FieldId, Msg, OperationKind, PageChanged, ResourceKind,
|
||||||
@ -97,7 +98,9 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
issue_bellow_id,
|
issue_bellow_id,
|
||||||
))) => crate::ws::issue::change_position(issue_bellow_id, model),
|
))) => crate::ws::issue::change_position(issue_bellow_id, model),
|
||||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragOverStatus(status))) => {
|
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragOverStatus(status))) => {
|
||||||
crate::ws::issue::change_status(status, model)
|
if !crate::ws::issue::change_status(status, model) {
|
||||||
|
orders.skip();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDropZone(_status))) => {
|
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDropZone(_status))) => {
|
||||||
crate::ws::issue::sync(model, orders)
|
crate::ws::issue::sync(model, orders)
|
||||||
@ -116,17 +119,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rebuild_visible {
|
if rebuild_visible {
|
||||||
let visible_issues = ProjectPage::visible_issues(
|
change_visible(model);
|
||||||
crate::match_page!(model, Project),
|
|
||||||
model.epics(),
|
|
||||||
model.issue_statuses(),
|
|
||||||
model
|
|
||||||
.issue_ids
|
|
||||||
.iter()
|
|
||||||
.filter_map(|id| model.issues_by_id.get(id)),
|
|
||||||
model.user(),
|
|
||||||
);
|
|
||||||
crate::match_page_mut!(model, Project).visible_issues = visible_issues;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDropZone(
|
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDropZone(
|
||||||
_issue_status_id,
|
_issue_status_id,
|
||||||
))) => {
|
))) => {
|
||||||
sync(model, orders);
|
// sync(model, orders);
|
||||||
}
|
}
|
||||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::EditIssueStatusName(
|
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::EditIssueStatusName(
|
||||||
id,
|
id,
|
||||||
|
@ -209,7 +209,6 @@ fn columns_section(model: &Model, page: &ProjectSettingsPage) -> Node<Msg> {
|
|||||||
.issue_ids
|
.issue_ids
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|id| model.issues_by_id.get(id))
|
.filter_map(|id| model.issues_by_id.get(id))
|
||||||
.iter()
|
|
||||||
.fold(
|
.fold(
|
||||||
HashMap::with_capacity(model.issue_statuses_by_id.len()),
|
HashMap::with_capacity(model.issue_statuses_by_id.len()),
|
||||||
|mut h, issue| {
|
|mut h, issue| {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use jirs_data::msg::WsMsgIssue;
|
use jirs_data::msg::{IssueSync, WsMsgIssue};
|
||||||
use jirs_data::*;
|
use jirs_data::*;
|
||||||
use seed::prelude::Orders;
|
use seed::prelude::Orders;
|
||||||
use seed::*;
|
use seed::*;
|
||||||
@ -30,59 +30,51 @@ pub fn change_position(below_id: EpicId, model: &mut Model) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (issue_status_id, epic_id) = model
|
let (issue_status_id, epic_id) = model
|
||||||
.issue_ids
|
.issues_by_id
|
||||||
.iter()
|
.get(&dragged_id)
|
||||||
.filter_map(|id| model.issues_by_id.get(id))
|
.map(|issue| (issue.issue_status_id, issue.epic_id))
|
||||||
.find_map(|issue| {
|
|
||||||
if issue.id == dragged_id {
|
|
||||||
Some((issue.issue_status_id, issue.epic_id))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let mut issues: Vec<Issue> = model
|
let mut issues = model
|
||||||
.issues_mut()
|
.issue_ids
|
||||||
.drain_filter(|issue| issue.issue_status_id == issue_status_id && issue.epic_id == epic_id)
|
.iter()
|
||||||
.collect();
|
.filter_map(|id| {
|
||||||
issues.sort_by(|a, b| a.list_position.cmp(&b.list_position));
|
model
|
||||||
|
.issues_by_id
|
||||||
|
.get(id)
|
||||||
|
.filter(|issue| {
|
||||||
|
issue.issue_status_id == issue_status_id && issue.epic_id == epic_id
|
||||||
|
})
|
||||||
|
.map(|issue| (issue.id, issue.list_position))
|
||||||
|
})
|
||||||
|
.collect::<Vec<(i32, i32)>>();
|
||||||
|
issues.sort_by(|(_, a), (_, b)| a.cmp(b));
|
||||||
|
|
||||||
|
{
|
||||||
let below_idx = issues
|
let below_idx = issues
|
||||||
.iter()
|
.iter()
|
||||||
.position(|issue| issue.id == below_id)
|
.position(|(id, _)| *id == below_id)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let dragged_idx = issues
|
let dragged_idx = issues
|
||||||
.iter()
|
.iter()
|
||||||
.position(|issue| issue.id == dragged_id)
|
.position(|(id, _)| *id == dragged_id)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let dragged = issues.remove(dragged_idx);
|
let dragged = issues.remove(dragged_idx);
|
||||||
issues.insert(below_idx, dragged);
|
issues.insert(below_idx, dragged);
|
||||||
|
}
|
||||||
|
|
||||||
let mut changed = Vec::with_capacity(issues.len());
|
{
|
||||||
for (idx, mut issue) in issues.into_iter().enumerate() {
|
for (idx, (id, _)) in issues.into_iter().enumerate() {
|
||||||
|
if let Some(issue) = model.issues_by_id.get_mut(&id) {
|
||||||
issue.list_position = idx as i32;
|
issue.list_position = idx as i32;
|
||||||
if let Some(iss) = model.issues_by_id.get_mut(&issue.id) {
|
|
||||||
iss.list_position = issue.list_position;
|
|
||||||
}
|
}
|
||||||
changed.push((issue.id, issue.list_position));
|
crate::match_page_mut!(model, Project)
|
||||||
model.issues_mut().push(issue);
|
.issue_drag
|
||||||
|
.mark_dirty(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let visible = ProjectPage::visible_issues(
|
change_visible(model);
|
||||||
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.visible_issues = visible;
|
|
||||||
for (id, _) in changed.iter() {
|
|
||||||
project_page.issue_drag.mark_dirty(*id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
pub fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||||
@ -91,19 +83,17 @@ pub fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let changes: Vec<(EpicId, ListPosition, IssueStatusId, Option<EpicId>)> = dirty
|
let changes = dirty
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|id| {
|
.filter_map(|id| {
|
||||||
model.issues_by_id.get(&id).map(|issue| {
|
model.issues_by_id.get(&id).map(|issue| IssueSync {
|
||||||
(
|
id: issue.id,
|
||||||
issue.id,
|
list_position: issue.list_position,
|
||||||
issue.list_position,
|
issue_status_id: issue.issue_status_id,
|
||||||
issue.issue_status_id,
|
epic_id: issue.epic_id,
|
||||||
issue.epic_id,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect::<Vec<IssueSync>>();
|
||||||
|
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::Issue(WsMsgIssue::IssueSyncListPosition(changes)),
|
WsMsg::Issue(WsMsgIssue::IssueSyncListPosition(changes)),
|
||||||
@ -113,15 +103,18 @@ pub fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
crate::match_page_mut!(model, Project).issue_drag.clear();
|
crate::match_page_mut!(model, Project).issue_drag.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_status(status_id: IssueStatusId, model: &mut Model) {
|
pub fn change_status(status_id: IssueStatusId, model: &mut Model) -> bool {
|
||||||
let dragged_id = match crate::match_page!(model, Project)
|
let dragged_id = match crate::match_page!(model, Project, false)
|
||||||
.issue_drag
|
.issue_drag
|
||||||
.dragged_id
|
.dragged_id
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
Some(issue_id) => issue_id,
|
Some(issue_id) => issue_id,
|
||||||
_ => return error!("Nothing is dragged"),
|
_ => {
|
||||||
|
error!("Nothing is dragged");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let (issue_status_id, epic_id) = model
|
let (issue_status_id, epic_id) = model
|
||||||
.issues_by_id
|
.issues_by_id
|
||||||
@ -129,45 +122,50 @@ pub fn change_status(status_id: IssueStatusId, model: &mut Model) {
|
|||||||
.map(|issue| (issue.issue_status_id, issue.epic_id))
|
.map(|issue| (issue.issue_status_id, issue.epic_id))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
if status_id == issue_status_id {
|
if status_id == issue_status_id {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut issues = {
|
let epic_and_status_issues_count = model
|
||||||
let mut h = std::mem::take(&mut model.issues_by_id);
|
.issue_ids
|
||||||
h.keys()
|
.iter()
|
||||||
.filter_map(|id| h.remove(id))
|
.filter_map(|id| {
|
||||||
.collect::<Vec<Issue>>()
|
model
|
||||||
}
|
.issues_by_id
|
||||||
.drain_filter(|issue| {
|
.get(id)
|
||||||
if issue.id == dragged_id {
|
.filter(|issue| same_epic_and_status(status_id, epic_id, issue))
|
||||||
issue.issue_status_id = status_id;
|
|
||||||
}
|
|
||||||
issue.issue_status_id == status_id && issue.epic_id == epic_id
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<Issue>>();
|
.count();
|
||||||
|
|
||||||
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;
|
|
||||||
if let Some(iss) = model.issues_by_id.get_mut(&issue.id) {
|
|
||||||
iss.issue_status_id = status_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dirty.push(issue.id);
|
|
||||||
model.issue_ids.push(issue.id);
|
|
||||||
model.issues_by_id.insert(issue.id, issue);
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
let project_page = crate::match_page_mut!(model, Project);
|
if let Some(issue) = model.issues_by_id.get_mut(&dragged_id) {
|
||||||
for id in dirty {
|
issue.issue_status_id = status_id;
|
||||||
|
issue.list_position = epic_and_status_issues_count as ListPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let issues_in_column = model
|
||||||
|
.issue_ids
|
||||||
|
.iter()
|
||||||
|
.filter_map(|id| {
|
||||||
|
model
|
||||||
|
.issues_by_id
|
||||||
|
.get(id)
|
||||||
|
.filter(|issue| same_epic_and_status(status_id, epic_id, issue))
|
||||||
|
.map(|issue| issue.id)
|
||||||
|
})
|
||||||
|
.collect::<Vec<i32>>();
|
||||||
|
|
||||||
|
{
|
||||||
|
let project_page = crate::match_page_mut!(model, Project, false);
|
||||||
|
for id in issues_in_column {
|
||||||
project_page.issue_drag.mark_dirty(id);
|
project_page.issue_drag.mark_dirty(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
change_visible(model);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_visible(model: &mut Model) {
|
||||||
let visible = ProjectPage::visible_issues(
|
let visible = ProjectPage::visible_issues(
|
||||||
crate::match_page!(model, Project),
|
crate::match_page!(model, Project),
|
||||||
model.epics(),
|
model.epics(),
|
||||||
@ -181,3 +179,7 @@ pub fn change_status(status_id: IssueStatusId, model: &mut Model) {
|
|||||||
|
|
||||||
crate::match_page_mut!(model, Project).visible_issues = visible;
|
crate::match_page_mut!(model, Project).visible_issues = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn same_epic_and_status(status_id: IssueStatusId, epic_id: Option<EpicId>, issue: &&Issue) -> bool {
|
||||||
|
issue.issue_status_id == status_id && issue.epic_id == epic_id
|
||||||
|
}
|
||||||
|
@ -253,21 +253,16 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
|||||||
// issues
|
// issues
|
||||||
WsMsg::Project(WsMsgProject::ProjectIssuesLoaded(v)) => {
|
WsMsg::Project(WsMsgProject::ProjectIssuesLoaded(v)) => {
|
||||||
v.sort_by(|a, b| (a.list_position as i64).cmp(&(b.list_position as i64)));
|
v.sort_by(|a, b| (a.list_position as i64).cmp(&(b.list_position as i64)));
|
||||||
{
|
let len = v.len();
|
||||||
model.issue_ids = Vec::with_capacity(v.len());
|
model.issue_ids = Vec::with_capacity(len);
|
||||||
model.issues_by_id =
|
model.issues_by_id =
|
||||||
std::mem::take(v)
|
std::mem::take(v)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(HashMap::with_capacity(v.len()), |h, o| {
|
.fold(HashMap::with_capacity(len), |mut h, o| {
|
||||||
model.issue_ids.push(o.id);
|
model.issue_ids.push(o.id);
|
||||||
h.insert(o.id, o);
|
h.insert(o.id, o);
|
||||||
h
|
h
|
||||||
});
|
});
|
||||||
};
|
|
||||||
model.issues_by_id.clear();
|
|
||||||
for issue in v {
|
|
||||||
model.issues_by_id.insert(issue.id, issue.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
orders.send_msg(Msg::ResourceChanged(
|
orders.send_msg(Msg::ResourceChanged(
|
||||||
ResourceKind::Issue,
|
ResourceKind::Issue,
|
||||||
@ -312,6 +307,15 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
|||||||
Some(*id),
|
Some(*id),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
WsMsg::Issue(WsMsgIssue::IssueSyncedListPosition(sync)) => {
|
||||||
|
for o in sync {
|
||||||
|
if let Some(issue) = model.issues_by_id.get_mut(&o.id) {
|
||||||
|
issue.list_position = o.list_position;
|
||||||
|
issue.issue_status_id = o.issue_status_id;
|
||||||
|
issue.epic_id = o.epic_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// users
|
// users
|
||||||
WsMsg::Project(WsMsgProject::ProjectUsersLoaded(v)) => {
|
WsMsg::Project(WsMsgProject::ProjectUsersLoaded(v)) => {
|
||||||
model.user_ids = v.iter().map(|u| u.id).collect();
|
model.user_ids = v.iter().map(|u| u.id).collect();
|
||||||
|
Loading…
Reference in New Issue
Block a user