Reduce model memory usage
This commit is contained in:
parent
5abcfb99a6
commit
d49c92cd53
@ -258,7 +258,7 @@ pub struct Issue {
|
||||
pub struct IssueStatus {
|
||||
pub id: IssueStatusId,
|
||||
pub name: String,
|
||||
pub position: ProjectId,
|
||||
pub position: ListPosition,
|
||||
pub project_id: ProjectId,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
|
@ -11,7 +11,7 @@ pub fn epic_field<Modal>(model: &Model, modal: &Modal, field_id: FieldId) -> Opt
|
||||
where
|
||||
Modal: IssueModal,
|
||||
{
|
||||
if model.epics.is_empty() {
|
||||
if model.epic_ids.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let input = StyledSelect {
|
||||
@ -19,10 +19,16 @@ where
|
||||
name: "epic",
|
||||
selected: vec![modal
|
||||
.epic_id_value()
|
||||
.and_then(|id| model.epics.iter().find(|epic| epic.id == id as EpicId))
|
||||
.and_then(|id| model.epics_by_id.get(&(id as EpicId)))
|
||||
.map(epic_select_option)
|
||||
.unwrap_or_default()],
|
||||
options: Some(model.epics.iter().map(epic_select_option)),
|
||||
options: Some(
|
||||
model
|
||||
.epic_ids
|
||||
.iter()
|
||||
.filter_map(|id| model.epics_by_id.get(id))
|
||||
.map(epic_select_option),
|
||||
),
|
||||
variant: SelectVariant::Normal,
|
||||
clearable: true,
|
||||
text_filter: modal.epic_state().text_filter.as_str(),
|
||||
|
@ -339,15 +339,13 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Comment(CommentFieldId::Body)),
|
||||
comment_id,
|
||||
)) => {
|
||||
let id = *comment_id;
|
||||
let body = model
|
||||
.comments
|
||||
.iter()
|
||||
.find(|c| c.id == id)
|
||||
.comments_by_id
|
||||
.get(comment_id)
|
||||
.map(|c| c.body.clone())
|
||||
.unwrap_or_default();
|
||||
modal.comment_form.body = body;
|
||||
modal.comment_form.id = Some(id);
|
||||
modal.comment_form.id = Some(*comment_id);
|
||||
modal.comment_form.creating = true;
|
||||
}
|
||||
Msg::DeleteComment(comment_id) => {
|
||||
|
@ -243,7 +243,11 @@ fn left_modal_column(model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
||||
vec![div![C!["fakeTextArea"], "Add a comment...", handler]]
|
||||
};
|
||||
|
||||
let comments = model.comments.iter().flat_map(|c| comment(model, modal, c));
|
||||
let comments = model
|
||||
.comment_ids
|
||||
.iter()
|
||||
.flat_map(|id| model.comments_by_id.get(id))
|
||||
.flat_map(|c| comment(model, modal, c));
|
||||
|
||||
div![
|
||||
C!["left"],
|
||||
@ -403,11 +407,22 @@ fn status_select(
|
||||
opened: status_state.opened,
|
||||
variant: SelectVariant::Normal,
|
||||
text_filter: status_state.text_filter.as_str(),
|
||||
options: Some(model.issue_statuses.iter().map(issue_status_select_option)),
|
||||
options: Some(
|
||||
model
|
||||
.issue_status_ids
|
||||
.iter()
|
||||
.filter_map(|id| model.issue_statuses_by_id.get(id))
|
||||
.map(issue_status_select_option),
|
||||
),
|
||||
selected: model
|
||||
.issue_statuses
|
||||
.issue_status_ids
|
||||
.iter()
|
||||
.filter(|is| is.id == payload.issue_status_id)
|
||||
.filter_map(|id| {
|
||||
model
|
||||
.issue_statuses_by_id
|
||||
.get(id)
|
||||
.filter(|is| is.id == payload.issue_status_id)
|
||||
})
|
||||
.map(issue_status_select_option)
|
||||
.collect(),
|
||||
|
||||
@ -424,7 +439,7 @@ fn status_select(
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn issue_status_select_option<'l>(is: &'l IssueStatus) -> StyledSelectOption<'l> {
|
||||
fn issue_status_select_option(is: &IssueStatus) -> StyledSelectOption<'_> {
|
||||
StyledSelectOption {
|
||||
value: is.id as u32,
|
||||
class_list: is.name.as_str(),
|
||||
|
@ -254,11 +254,11 @@ pub struct Model {
|
||||
pub user_settings: Option<UserSetting>,
|
||||
|
||||
// comments
|
||||
pub comments: Vec<Comment>,
|
||||
pub comment_ids: Vec<CommentId>,
|
||||
pub comments_by_id: HashMap<CommentId, Comment>,
|
||||
|
||||
// issue_statuses
|
||||
pub issue_statuses: Vec<IssueStatus>,
|
||||
pub issue_status_ids: Vec<IssueStatusId>,
|
||||
pub issue_statuses_by_id: HashMap<IssueStatusId, IssueStatus>,
|
||||
pub issue_statuses_by_name: HashMap<String, IssueStatus>,
|
||||
|
||||
@ -273,7 +273,7 @@ pub struct Model {
|
||||
pub projects: Vec<Project>,
|
||||
|
||||
// epics
|
||||
pub epics: Vec<Epic>,
|
||||
pub epic_ids: Vec<EpicId>,
|
||||
pub epics_by_id: HashMap<EpicId, Epic>,
|
||||
|
||||
pub key_triggers: std::rc::Rc<std::cell::RefCell<HashMap<char, Box<dyn BuildMsg>>>>,
|
||||
@ -304,15 +304,15 @@ impl Model {
|
||||
user_ids: vec![],
|
||||
users_by_id: HashMap::with_capacity(1_000),
|
||||
user_settings: None,
|
||||
comments: vec![],
|
||||
comment_ids: vec![],
|
||||
comments_by_id: HashMap::with_capacity(1_000),
|
||||
issue_statuses: vec![],
|
||||
issue_status_ids: vec![],
|
||||
issue_statuses_by_id: HashMap::with_capacity(1_000),
|
||||
issue_statuses_by_name: HashMap::with_capacity(1_000),
|
||||
messages: vec![],
|
||||
user_projects: vec![],
|
||||
projects: vec![],
|
||||
epics: vec![],
|
||||
epic_ids: vec![],
|
||||
issues_by_id: HashMap::with_capacity(1_000),
|
||||
show_extras: false,
|
||||
epics_by_id: HashMap::with_capacity(1_000),
|
||||
@ -323,16 +323,6 @@ impl Model {
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
|
@ -11,38 +11,42 @@ use crate::Msg;
|
||||
pub fn view(model: &Model) -> Node<Msg> {
|
||||
let page = crate::match_page!(model, Epics; Empty);
|
||||
|
||||
let epics = model.epics.iter().map(|epic| {
|
||||
let issues = page.issues(epic.id).map(|v| {
|
||||
v.iter()
|
||||
.filter_map(|i| model.issues_by_id.get(i))
|
||||
.map(|issue| {
|
||||
render_issue(
|
||||
issue,
|
||||
model.issue_statuses_by_id.get(&issue.issue_status_id),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Node<Msg>>>()
|
||||
});
|
||||
let epics = model
|
||||
.epic_ids
|
||||
.iter()
|
||||
.filter_map(|id| model.epics_by_id.get(id))
|
||||
.map(|epic| {
|
||||
let issues = page.issues(epic.id).map(|v| {
|
||||
v.iter()
|
||||
.filter_map(|i| model.issues_by_id.get(i))
|
||||
.map(|issue| {
|
||||
render_issue(
|
||||
issue,
|
||||
model.issue_statuses_by_id.get(&issue.issue_status_id),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Node<Msg>>>()
|
||||
});
|
||||
|
||||
li![
|
||||
C!["epic"],
|
||||
div![
|
||||
C!["firstRow"],
|
||||
div![C!["epicName"], &epic.name],
|
||||
li![
|
||||
C!["epic"],
|
||||
div![
|
||||
C!["date"],
|
||||
date_field("Starts at:", "startsAt", epic.starts_at.as_ref()),
|
||||
date_field("Ends at:", "endsAt", epic.ends_at.as_ref()),
|
||||
C!["firstRow"],
|
||||
div![C!["epicName"], &epic.name],
|
||||
div![
|
||||
C!["date"],
|
||||
date_field("Starts at:", "startsAt", epic.starts_at.as_ref()),
|
||||
date_field("Ends at:", "endsAt", epic.ends_at.as_ref()),
|
||||
],
|
||||
div![
|
||||
C!["counter"],
|
||||
"Number of issues:",
|
||||
issues.as_ref().map(Vec::len).unwrap_or(0)
|
||||
],
|
||||
],
|
||||
div![
|
||||
C!["counter"],
|
||||
"Number of issues:",
|
||||
issues.as_ref().map(Vec::len).unwrap_or(0)
|
||||
],
|
||||
],
|
||||
div![C!["secondRow"], div![C!["issues"], issues]]
|
||||
]
|
||||
});
|
||||
div![C!["secondRow"], div![C!["issues"], issues]]
|
||||
]
|
||||
});
|
||||
|
||||
inner_layout(
|
||||
model,
|
||||
|
@ -29,24 +29,26 @@ pub struct ProjectPage {
|
||||
}
|
||||
|
||||
impl ProjectPage {
|
||||
pub fn visible_issues<'issue, IssueStream>(
|
||||
pub fn visible_issues<'model, IssueStream, IssueStatusStream, EpicStream>(
|
||||
page: &ProjectPage,
|
||||
epics: &[Epic],
|
||||
statuses: &[IssueStatus],
|
||||
num_of_epics: usize,
|
||||
epics: EpicStream,
|
||||
statuses: IssueStatusStream,
|
||||
issues: IssueStream,
|
||||
user: &Option<User>,
|
||||
) -> Vec<EpicIssuePerStatus>
|
||||
where
|
||||
IssueStream: std::iter::Iterator<Item = &'issue Issue>,
|
||||
IssueStream: std::iter::Iterator<Item = &'model Issue>,
|
||||
IssueStatusStream: std::iter::Iterator<Item = &'model IssueStatus>,
|
||||
EpicStream: std::iter::Iterator<Item = &'model Epic>,
|
||||
{
|
||||
let num_of_epics = epics.len();
|
||||
let epics = vec![None].into_iter().chain(
|
||||
epics
|
||||
.iter()
|
||||
.map(|epic| Some((epic.id, epic.name.as_str(), epic.starts_at, epic.ends_at))),
|
||||
epics.map(|epic| Some((epic.id, epic.name.as_str(), epic.starts_at, epic.ends_at))),
|
||||
);
|
||||
|
||||
let statuses = statuses.iter().map(|s| (s.id, s.name.as_str()));
|
||||
let statuses = statuses
|
||||
.map(|s| (s.id, s.name.as_str()))
|
||||
.collect::<Vec<(IssueStatusId, &str)>>();
|
||||
let issues = issues.filter(|issue| {
|
||||
issue_filter_with_avatars(issue, &page.active_avatar_filters)
|
||||
&& issue_filter_with_text(issue, page.text_filter.as_str())
|
||||
@ -93,15 +95,15 @@ impl ProjectPage {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
for (current_status_id, issue_status_name) in statuses.to_owned() {
|
||||
for (current_status_id, issue_status_name) in statuses.iter() {
|
||||
let per_status_map = StatusIssueIds {
|
||||
status_id: current_status_id,
|
||||
status_id: *current_status_id,
|
||||
status_name: issue_status_name.to_string(),
|
||||
issue_ids: issues_per_epic_id
|
||||
.get(&epic.map(|(id, ..)| id))
|
||||
.map(|v| {
|
||||
v.iter()
|
||||
.filter(|issue| issue_filter_status(issue, current_status_id))
|
||||
.filter(|issue| issue_filter_status(issue, *current_status_id))
|
||||
.map(|issue| issue.id)
|
||||
.collect()
|
||||
})
|
||||
|
@ -7,7 +7,7 @@ use seed::prelude::Orders;
|
||||
use crate::components::styled_select::StyledSelectChanged;
|
||||
use crate::model::{Model, Page, PageContent};
|
||||
use crate::pages::project_settings_page::ProjectSettingsPage;
|
||||
use crate::ws::{board_load, send_ws_msg};
|
||||
use crate::ws::{board_load, send_ws_msg, sort_issue_statuses};
|
||||
use crate::{match_page_mut, FieldId, Msg, PageChanged, ProjectPageChange, WebSocketChanged};
|
||||
|
||||
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
@ -114,7 +114,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
))) => page.column_drag.clear_last(),
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(
|
||||
ProjectPageChange::ColumnExchangePosition(issue_bellow_id),
|
||||
)) => exchange_position(issue_bellow_id, model),
|
||||
)) => swap_position(issue_bellow_id, model),
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDropZone(
|
||||
_issue_status_id,
|
||||
))) => {
|
||||
@ -124,39 +124,35 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
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(
|
||||
WsMsgIssueStatus::IssueStatusUpdate(id, name, pos).into(),
|
||||
model.ws.as_ref(),
|
||||
orders,
|
||||
);
|
||||
if let Some(old_id) = page.edit_column_id {
|
||||
let name = page.name.value.clone();
|
||||
if let Some((id, pos)) = model
|
||||
.issue_statuses_by_id
|
||||
.get(&old_id)
|
||||
.map(|is| (is.id, is.position))
|
||||
{
|
||||
send_ws_msg(
|
||||
WsMsgIssueStatus::IssueStatusUpdate(id, name, pos).into(),
|
||||
model.ws.as_ref(),
|
||||
orders,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
page.name.value = model
|
||||
.issue_statuses
|
||||
.iter()
|
||||
.find_map(|is| {
|
||||
if Some(is.id) == id {
|
||||
Some(is.name.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or_default();
|
||||
if let Some(id) = id {
|
||||
page.name.value = model
|
||||
.issue_statuses_by_id
|
||||
.get(&id)
|
||||
.map(|is| is.name.clone())
|
||||
.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 position = model.issue_status_ids.len();
|
||||
let ws_msg = WsMsgIssueStatus::IssueStatusCreate(name, position as i32).into();
|
||||
send_ws_msg(ws_msg, model.ws.as_ref(), orders);
|
||||
}
|
||||
@ -164,11 +160,8 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) {
|
||||
let page = match &mut model.page_content {
|
||||
PageContent::ProjectSettings(page) => page,
|
||||
_ => return,
|
||||
};
|
||||
fn swap_position(bellow_id: IssueStatusId, model: &mut Model) {
|
||||
let page = crate::match_page_mut!(model, ProjectSettings);
|
||||
if page.column_drag.dragged_or_last(bellow_id) {
|
||||
return;
|
||||
}
|
||||
@ -177,41 +170,29 @@ fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) {
|
||||
_ => return log::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);
|
||||
let bellow = model
|
||||
.issue_statuses_by_id
|
||||
.get(&bellow_id)
|
||||
.map(|is| is.position)
|
||||
.unwrap_or_default();
|
||||
let dragged = model
|
||||
.issue_statuses_by_id
|
||||
.get(&dragged_id)
|
||||
.map(|is| is.position)
|
||||
.unwrap_or_default();
|
||||
|
||||
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),
|
||||
};
|
||||
if let Some(is) = model.issue_statuses_by_id.get_mut(&dragged_id) {
|
||||
is.position = bellow;
|
||||
}
|
||||
if let Some(is) = model.issue_statuses_by_id.get_mut(&bellow_id) {
|
||||
is.position = dragged;
|
||||
}
|
||||
|
||||
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(bellow_id);
|
||||
|
||||
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);
|
||||
sort_issue_statuses(model);
|
||||
}
|
||||
|
||||
fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
@ -224,11 +205,10 @@ fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
_ => return log::error!("bad content type"),
|
||||
};
|
||||
for id in dirty {
|
||||
let IssueStatus { name, position, .. } =
|
||||
match model.issue_statuses.iter().find(|is| is.id == id) {
|
||||
Some(is) => is,
|
||||
_ => continue,
|
||||
};
|
||||
let IssueStatus { name, position, .. } = match model.issue_statuses_by_id.get(&id) {
|
||||
Some(is) => is,
|
||||
_ => continue,
|
||||
};
|
||||
send_ws_msg(
|
||||
WsMsgIssueStatus::IssueStatusUpdate(id, name.clone(), *position).into(),
|
||||
model.ws.as_ref(),
|
||||
|
@ -203,7 +203,7 @@ fn category_select_option<'l>(pc: ProjectCategory) -> StyledSelectOption<'l> {
|
||||
/// Build draggable columns preview with option to remove and add new columns
|
||||
#[inline(always)]
|
||||
fn columns_section(model: &Model, page: &ProjectSettingsPage) -> Node<Msg> {
|
||||
let width = 100f64 / (model.issue_statuses.len() + 1) as f64;
|
||||
let width = 100f64 / (model.issue_status_ids.len() + 1) as f64;
|
||||
let column_style = format!("width: calc({width}% - 10px)", width = width);
|
||||
let per_column_issue_count = model
|
||||
.issue_ids
|
||||
@ -216,11 +216,11 @@ fn columns_section(model: &Model, page: &ProjectSettingsPage) -> Node<Msg> {
|
||||
h
|
||||
},
|
||||
);
|
||||
let columns: Vec<Node<Msg>> = model
|
||||
.issue_statuses
|
||||
let columns = model
|
||||
.issue_status_ids
|
||||
.iter()
|
||||
.map(|is| column_preview(is, page, &per_column_issue_count, column_style.as_str()))
|
||||
.collect();
|
||||
.filter_map(|id| model.issue_statuses_by_id.get(id))
|
||||
.map(|is| column_preview(is, page, &per_column_issue_count, column_style.as_str()));
|
||||
|
||||
let columns_section = section![
|
||||
C!["columnsSection"],
|
||||
|
@ -81,7 +81,7 @@ pub fn render(model: &Model) -> Vec<Node<Msg>> {
|
||||
},
|
||||
);
|
||||
|
||||
let issue_nav = if model.issue_statuses.is_empty() {
|
||||
let issue_nav = if model.issue_status_ids.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
vec![
|
||||
|
@ -168,8 +168,15 @@ pub fn change_status(status_id: IssueStatusId, model: &mut Model) -> bool {
|
||||
pub fn change_visible(model: &mut Model) {
|
||||
let visible = ProjectPage::visible_issues(
|
||||
crate::match_page!(model, Project),
|
||||
model.epics(),
|
||||
model.issue_statuses(),
|
||||
model.epic_ids.len(),
|
||||
model
|
||||
.epic_ids
|
||||
.iter()
|
||||
.filter_map(|id| model.epics_by_id.get(id)),
|
||||
model
|
||||
.issue_status_ids
|
||||
.iter()
|
||||
.filter_map(|id| model.issue_statuses_by_id.get(id)),
|
||||
model
|
||||
.issue_ids
|
||||
.iter()
|
||||
|
@ -197,10 +197,20 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
|
||||
// issue statuses
|
||||
WsMsg::IssueStatus(WsMsgIssueStatus::IssueStatusesLoaded(v)) => {
|
||||
model.issue_statuses = std::mem::take(v);
|
||||
model
|
||||
.issue_statuses
|
||||
.sort_by(|a, b| a.position.cmp(&b.position));
|
||||
let len = v.len();
|
||||
let mut issue_statuses = std::mem::take(v);
|
||||
issue_statuses.sort_by(|a, b| a.position.cmp(&b.position));
|
||||
model.issue_status_ids = Vec::with_capacity(len);
|
||||
|
||||
model.issue_statuses_by_id =
|
||||
issue_statuses
|
||||
.into_iter()
|
||||
.fold(HashMap::with_capacity(len), |mut h, is| {
|
||||
model.issue_status_ids.push(is.id);
|
||||
h.insert(is.id, is);
|
||||
h
|
||||
});
|
||||
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::IssueStatus,
|
||||
OperationKind::ListLoaded,
|
||||
@ -208,42 +218,33 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
));
|
||||
}
|
||||
WsMsg::IssueStatus(WsMsgIssueStatus::IssueStatusCreated(is)) => {
|
||||
let id = is.id;
|
||||
model.issue_statuses.push(is.clone());
|
||||
model
|
||||
.issue_statuses
|
||||
.sort_by(|a, b| a.position.cmp(&b.position));
|
||||
model.issue_status_ids.push(is.id);
|
||||
model.issue_statuses_by_id.insert(is.id, is.clone());
|
||||
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::IssueStatus,
|
||||
OperationKind::SingleCreated,
|
||||
Some(id),
|
||||
Some(is.id),
|
||||
));
|
||||
}
|
||||
WsMsg::IssueStatus(WsMsgIssueStatus::IssueStatusUpdated(changed)) => {
|
||||
let id = changed.id;
|
||||
if let Some(idx) = model.issue_statuses.iter().position(|c| c.id == changed.id) {
|
||||
std::mem::swap(&mut model.issue_statuses[idx], changed);
|
||||
}
|
||||
model
|
||||
.issue_statuses
|
||||
.sort_by(|a, b| a.position.cmp(&b.position));
|
||||
.issue_statuses_by_id
|
||||
.insert(changed.id, changed.clone());
|
||||
sort_issue_statuses(model);
|
||||
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::IssueStatus,
|
||||
OperationKind::SingleModified,
|
||||
Some(id),
|
||||
Some(changed.id),
|
||||
));
|
||||
}
|
||||
WsMsg::IssueStatus(WsMsgIssueStatus::IssueStatusDeleted(dropped_id, _count)) => {
|
||||
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));
|
||||
model.issue_statuses_by_id.remove(dropped_id);
|
||||
model.issue_status_ids = std::mem::take(&mut model.issue_status_ids)
|
||||
.into_iter()
|
||||
.filter(|id| id != dropped_id)
|
||||
.collect();
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::IssueStatus,
|
||||
OperationKind::SingleRemoved,
|
||||
@ -315,6 +316,11 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
issue.epic_id = o.epic_id;
|
||||
}
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Issue,
|
||||
OperationKind::ListLoaded,
|
||||
None,
|
||||
));
|
||||
}
|
||||
// users
|
||||
WsMsg::Project(WsMsgProject::ProjectUsersLoaded(v)) => {
|
||||
@ -335,12 +341,12 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
Some(modal) => modal.id,
|
||||
_ => return,
|
||||
};
|
||||
comments.sort_by(|a, b| a.updated_at.cmp(&b.updated_at));
|
||||
if comments.iter().any(|c| c.issue_id != issue_id) {
|
||||
return;
|
||||
}
|
||||
comments.sort_by(|a, b| a.updated_at.cmp(&b.updated_at));
|
||||
model.comments = comments.clone();
|
||||
for comment in std::mem::take(comments) {
|
||||
model.comment_ids.push(comment.id);
|
||||
model.comments_by_id.insert(comment.id, comment);
|
||||
}
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
@ -351,10 +357,7 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
}
|
||||
WsMsg::Comment(WsMsgComment::CommentUpdated(comment)) => {
|
||||
let comment_id = comment.id;
|
||||
if let Some(idx) = model.comments.iter().position(|c| c.id == comment.id) {
|
||||
let _ = std::mem::replace(&mut model.comments[idx], comment.clone());
|
||||
model.comments_by_id.insert(comment.id, comment.clone());
|
||||
}
|
||||
model.comments_by_id.insert(comment.id, comment.clone());
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Comment,
|
||||
OperationKind::SingleModified,
|
||||
@ -362,8 +365,8 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
));
|
||||
}
|
||||
WsMsg::Comment(WsMsgComment::CommentDeleted(comment_id, _count)) => {
|
||||
if let Some(idx) = model.comments.iter().position(|c| c.id == *comment_id) {
|
||||
model.comments.remove(idx);
|
||||
if let Some(idx) = model.comment_ids.iter().position(|id| *id == *comment_id) {
|
||||
model.comment_ids.remove(idx);
|
||||
}
|
||||
model.comments_by_id.remove(&comment_id);
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
@ -407,10 +410,19 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
|
||||
// epics
|
||||
WsMsg::Epic(WsMsgEpic::EpicsLoaded(epics)) => {
|
||||
model.epics = epics.clone();
|
||||
for epic in epics {
|
||||
model.epics_by_id.insert(epic.id, epic.clone());
|
||||
}
|
||||
let epics = std::mem::take(epics);
|
||||
let len = epics.len();
|
||||
|
||||
model.epic_ids = Vec::with_capacity(len);
|
||||
model.epics_by_id =
|
||||
epics
|
||||
.into_iter()
|
||||
.fold(HashMap::with_capacity(len), |mut h, epic| {
|
||||
model.epic_ids.push(epic.id);
|
||||
h.insert(epic.id, epic);
|
||||
h
|
||||
});
|
||||
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
OperationKind::ListLoaded,
|
||||
@ -419,9 +431,10 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
}
|
||||
WsMsg::Epic(WsMsgEpic::EpicCreated(epic)) => {
|
||||
let id = epic.id;
|
||||
model.epics.push(epic.clone());
|
||||
model.epics.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
model.epic_ids.push(epic.id);
|
||||
model.epic_ids.sort();
|
||||
model.epics_by_id.insert(epic.id, epic.clone());
|
||||
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
OperationKind::SingleCreated,
|
||||
@ -430,11 +443,8 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
}
|
||||
WsMsg::Epic(WsMsgEpic::EpicUpdated(epic)) => {
|
||||
let epic_id = epic.id;
|
||||
if let Some(idx) = model.epics.iter().position(|e| e.id == epic.id) {
|
||||
let _ = std::mem::replace(&mut model.epics[idx], epic.clone());
|
||||
}
|
||||
model.epics_by_id.insert(epic.id, epic.clone());
|
||||
model.epics.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
OperationKind::SingleModified,
|
||||
@ -442,11 +452,11 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
));
|
||||
}
|
||||
WsMsg::Epic(WsMsgEpic::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));
|
||||
model.epic_ids = std::mem::take(&mut model.epic_ids)
|
||||
.into_iter()
|
||||
.filter(|epic_id| epic_id != id)
|
||||
.collect();
|
||||
orders.send_msg(Msg::ResourceChanged(
|
||||
ResourceKind::Epic,
|
||||
OperationKind::SingleRemoved,
|
||||
@ -481,6 +491,17 @@ pub fn update(msg: &mut WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn sort_issue_statuses(model: &mut Model) {
|
||||
let mut ids = model
|
||||
.issue_status_ids
|
||||
.iter()
|
||||
.filter_map(|id| model.issue_statuses_by_id.get(id))
|
||||
.map(|is| (is.id, is.position))
|
||||
.collect::<Vec<_>>();
|
||||
ids.sort_by(|(_, a), (_, b)| a.cmp(b));
|
||||
model.issue_status_ids = ids.into_iter().map(|(id, _)| id).collect();
|
||||
}
|
||||
|
||||
fn init_current_project(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
if model.projects.is_empty() {
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user