Add epic, display epics select. Filter epics
This commit is contained in:
parent
02a2ecdd01
commit
38654fca85
@ -68,6 +68,7 @@ impl std::fmt::Display for FieldId {
|
|||||||
EditIssueModalSection::Issue(IssueFieldId::ListPosition) => {
|
EditIssueModalSection::Issue(IssueFieldId::ListPosition) => {
|
||||||
f.write_str("editIssue-listPosition")
|
f.write_str("editIssue-listPosition")
|
||||||
}
|
}
|
||||||
|
EditIssueModalSection::Issue(IssueFieldId::Epic) => f.write_str("editIssue-epic"),
|
||||||
},
|
},
|
||||||
FieldId::AddIssueModal(sub) => match sub {
|
FieldId::AddIssueModal(sub) => match sub {
|
||||||
IssueFieldId::Type => f.write_str("issueTypeAddIssueModal"),
|
IssueFieldId::Type => f.write_str("issueTypeAddIssueModal"),
|
||||||
@ -81,6 +82,7 @@ impl std::fmt::Display for FieldId {
|
|||||||
IssueFieldId::TimeSpent => f.write_str("addIssueModal-timeSpend"),
|
IssueFieldId::TimeSpent => f.write_str("addIssueModal-timeSpend"),
|
||||||
IssueFieldId::TimeRemaining => f.write_str("addIssueModal-timeRemaining"),
|
IssueFieldId::TimeRemaining => f.write_str("addIssueModal-timeRemaining"),
|
||||||
IssueFieldId::ListPosition => f.write_str("addIssueModal-listPosition"),
|
IssueFieldId::ListPosition => f.write_str("addIssueModal-listPosition"),
|
||||||
|
IssueFieldId::Epic => f.write_str("addIssueModal-epic"),
|
||||||
},
|
},
|
||||||
FieldId::TextFilterBoard => f.write_str("textFilterBoard"),
|
FieldId::TextFilterBoard => f.write_str("textFilterBoard"),
|
||||||
FieldId::CopyButtonLabel => f.write_str("copyButtonLabel"),
|
FieldId::CopyButtonLabel => f.write_str("copyButtonLabel"),
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#![feature(or_patterns)]
|
||||||
|
|
||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
use web_sys::File;
|
use web_sys::File;
|
||||||
|
|
||||||
@ -80,6 +82,11 @@ pub enum Msg {
|
|||||||
AddIssue,
|
AddIssue,
|
||||||
DeleteIssue(IssueId),
|
DeleteIssue(IssueId),
|
||||||
|
|
||||||
|
// epics
|
||||||
|
AddEpic,
|
||||||
|
DeleteEpic,
|
||||||
|
UpdateEpic,
|
||||||
|
|
||||||
// issue statuses
|
// issue statuses
|
||||||
DeleteIssueStatus(IssueStatusId),
|
DeleteIssueStatus(IssueStatusId),
|
||||||
|
|
||||||
@ -130,19 +137,18 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
|||||||
ws::update(ws_msg, model, orders);
|
ws::update(ws_msg, model, orders);
|
||||||
}
|
}
|
||||||
WebSocketChanged::WebSocketMessageLoaded(v) => {
|
WebSocketChanged::WebSocketMessageLoaded(v) => {
|
||||||
if let Ok(m) = bincode::deserialize(v.as_slice()) {
|
match bincode::deserialize(v.as_slice()) {
|
||||||
match m {
|
Ok(WsMsg::Ping | WsMsg::Pong) => {
|
||||||
WsMsg::Ping | WsMsg::Pong => {
|
orders.perform_cmd(cmds::timeout(1000, || {
|
||||||
orders.perform_cmd(cmds::timeout(1000, || {
|
Msg::WebSocketChange(WebSocketChanged::SendPing)
|
||||||
Msg::WebSocketChange(WebSocketChanged::SendPing)
|
}));
|
||||||
}));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
orders
|
|
||||||
.skip()
|
|
||||||
.send_msg(Msg::WebSocketChange(WebSocketChanged::WsMsg(m)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Ok(m) => {
|
||||||
|
orders
|
||||||
|
.skip()
|
||||||
|
.send_msg(Msg::WebSocketChange(WebSocketChanged::WsMsg(m)));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -309,7 +315,7 @@ fn authorize_or_redirect(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
let pathname = seed::document().location().unwrap().pathname().unwrap();
|
let pathname = seed::document().location().unwrap().pathname().unwrap();
|
||||||
match crate::shared::read_auth_token() {
|
match crate::shared::read_auth_token() {
|
||||||
Ok(token) => {
|
Ok(token) => {
|
||||||
send_ws_msg(WsMsg::AuthorizeRequest(token), model.ws.as_ref(), orders);
|
send_ws_msg(WsMsg::AuthorizeLoad(token), model.ws.as_ref(), orders);
|
||||||
}
|
}
|
||||||
Err(..) => {
|
Err(..) => {
|
||||||
match pathname.as_str() {
|
match pathname.as_str() {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
use jirs_data::{IssueFieldId, WsMsg};
|
use jirs_data::{EpicId, IssueFieldId, WsMsg};
|
||||||
use jirs_data::{IssuePriority, IssueType, ToVec};
|
use jirs_data::{IssuePriority, ToVec};
|
||||||
|
|
||||||
use crate::model::{AddIssueModal, ModalType, Model};
|
use crate::model::{AddIssueModal, ModalType, Model};
|
||||||
use crate::shared::styled_button::StyledButton;
|
use crate::shared::styled_button::StyledButton;
|
||||||
@ -11,11 +11,99 @@ use crate::shared::styled_input::StyledInput;
|
|||||||
use crate::shared::styled_modal::{StyledModal, Variant as ModalVariant};
|
use crate::shared::styled_modal::{StyledModal, Variant as ModalVariant};
|
||||||
use crate::shared::styled_select::StyledSelect;
|
use crate::shared::styled_select::StyledSelect;
|
||||||
use crate::shared::styled_select::StyledSelectChange;
|
use crate::shared::styled_select::StyledSelectChange;
|
||||||
|
use crate::shared::styled_select_child::{StyledSelectChild, StyledSelectChildBuilder};
|
||||||
use crate::shared::styled_textarea::StyledTextarea;
|
use crate::shared::styled_textarea::StyledTextarea;
|
||||||
use crate::shared::{ToChild, ToNode};
|
use crate::shared::{ToChild, ToNode};
|
||||||
use crate::ws::send_ws_msg;
|
use crate::ws::send_ws_msg;
|
||||||
use crate::{FieldId, Msg, WebSocketChanged};
|
use crate::{FieldId, Msg, WebSocketChanged};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum Type {
|
||||||
|
Task,
|
||||||
|
Bug,
|
||||||
|
Story,
|
||||||
|
Epic,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for Type {
|
||||||
|
fn from(n: u32) -> Self {
|
||||||
|
match n {
|
||||||
|
0 => Type::Task,
|
||||||
|
1 => Type::Bug,
|
||||||
|
2 => Type::Story,
|
||||||
|
3 => Type::Epic,
|
||||||
|
_ => Type::Task,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
fn ordered<'l>() -> &'l [Type] {
|
||||||
|
use Type::*;
|
||||||
|
&[Task, Bug, Story, Epic]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_label(&self) -> &str {
|
||||||
|
use Type::*;
|
||||||
|
match self {
|
||||||
|
Epic => "Create epic",
|
||||||
|
Bug | Task | Story => "Create issue",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn form_label(&self) -> &str {
|
||||||
|
use Type::*;
|
||||||
|
match self {
|
||||||
|
Epic => "Create epic",
|
||||||
|
Bug | Task | Story => "Create issue",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_action(&self) -> Msg {
|
||||||
|
use Type::*;
|
||||||
|
match self {
|
||||||
|
Epic => Msg::AddEpic,
|
||||||
|
Bug | Task | Story => Msg::AddIssue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToChild for Type {
|
||||||
|
type Builder = StyledSelectChildBuilder;
|
||||||
|
|
||||||
|
fn to_child(&self) -> Self::Builder {
|
||||||
|
let name = match self {
|
||||||
|
Type::Task => "Task",
|
||||||
|
Type::Bug => "Bug",
|
||||||
|
Type::Story => "Story",
|
||||||
|
Type::Epic => "Epic",
|
||||||
|
};
|
||||||
|
let value = match self {
|
||||||
|
Type::Task => 0,
|
||||||
|
Type::Bug => 1,
|
||||||
|
Type::Story => 2,
|
||||||
|
Type::Epic => 3,
|
||||||
|
};
|
||||||
|
let icon = match self {
|
||||||
|
Type::Task => crate::shared::styled_icon::Icon::Task,
|
||||||
|
Type::Bug => crate::shared::styled_icon::Icon::Bug,
|
||||||
|
Type::Story => crate::shared::styled_icon::Icon::Story,
|
||||||
|
Type::Epic => crate::shared::styled_icon::Icon::Epic,
|
||||||
|
};
|
||||||
|
|
||||||
|
let type_icon = crate::shared::styled_icon::StyledIcon::build(icon)
|
||||||
|
.add_class(name)
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
|
||||||
|
StyledSelectChild::build()
|
||||||
|
.add_class(name)
|
||||||
|
.text(name)
|
||||||
|
.icon(type_icon)
|
||||||
|
.value(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
||||||
let modal = model.modals.iter_mut().find(|modal| match modal {
|
let modal = model.modals.iter_mut().find(|modal| match modal {
|
||||||
ModalType::AddIssue(..) => true,
|
ModalType::AddIssue(..) => true,
|
||||||
@ -31,33 +119,50 @@ pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orde
|
|||||||
modal.reporter_state.update(msg, orders);
|
modal.reporter_state.update(msg, orders);
|
||||||
modal.type_state.update(msg, orders);
|
modal.type_state.update(msg, orders);
|
||||||
modal.priority_state.update(msg, orders);
|
modal.priority_state.update(msg, orders);
|
||||||
|
modal.epic_state.update(msg, orders);
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
Msg::AddIssue => {
|
Msg::AddEpic => {
|
||||||
let user_id = model.user.as_ref().map(|u| u.id).unwrap_or_default();
|
|
||||||
let project_id = model.project.as_ref().map(|p| p.id).unwrap_or_default();
|
|
||||||
|
|
||||||
let payload = jirs_data::CreateIssuePayload {
|
|
||||||
title: modal.title_state.value.clone(),
|
|
||||||
issue_type: modal.issue_type,
|
|
||||||
issue_status_id: modal.issue_status_id,
|
|
||||||
priority: modal.priority,
|
|
||||||
description: modal.description.clone(),
|
|
||||||
description_text: modal.description_text.clone(),
|
|
||||||
estimate: modal.estimate,
|
|
||||||
time_spent: modal.time_spent,
|
|
||||||
time_remaining: modal.time_remaining,
|
|
||||||
project_id: modal.project_id.unwrap_or(project_id),
|
|
||||||
user_ids: modal.user_ids.clone(),
|
|
||||||
reporter_id: modal.reporter_id.unwrap_or_else(|| user_id),
|
|
||||||
epic_id: modal.epic_id,
|
|
||||||
};
|
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
jirs_data::WsMsg::IssueCreateRequest(payload),
|
WsMsg::EpicCreate(modal.title_state.value.clone()),
|
||||||
model.ws.as_ref(),
|
model.ws.as_ref(),
|
||||||
orders,
|
orders,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Msg::AddIssue => {
|
||||||
|
let user_id = model.user.as_ref().map(|u| u.id).unwrap_or_default();
|
||||||
|
let project_id = model.project.as_ref().map(|p| p.id).unwrap_or_default();
|
||||||
|
let type_value = modal.type_state.values.get(0).cloned().unwrap_or_default();
|
||||||
|
match type_value {
|
||||||
|
0 | 1 | 2 => {
|
||||||
|
let issue_type = type_value.into();
|
||||||
|
let payload = jirs_data::CreateIssuePayload {
|
||||||
|
title: modal.title_state.value.clone(),
|
||||||
|
issue_type,
|
||||||
|
issue_status_id: modal.issue_status_id,
|
||||||
|
priority: modal.priority,
|
||||||
|
description: modal.description.clone(),
|
||||||
|
description_text: modal.description_text.clone(),
|
||||||
|
estimate: modal.estimate,
|
||||||
|
time_spent: modal.time_spent,
|
||||||
|
time_remaining: modal.time_remaining,
|
||||||
|
project_id: modal.project_id.unwrap_or(project_id),
|
||||||
|
user_ids: modal.user_ids.clone(),
|
||||||
|
reporter_id: modal.reporter_id.unwrap_or_else(|| user_id),
|
||||||
|
epic_id: modal.epic_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
send_ws_msg(
|
||||||
|
jirs_data::WsMsg::IssueCreate(payload),
|
||||||
|
model.ws.as_ref(),
|
||||||
|
orders,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueCreated(issue))) => {
|
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueCreated(issue))) => {
|
||||||
model.issues.push(issue.clone());
|
model.issues.push(issue.clone());
|
||||||
orders.skip().send_msg(Msg::ModalDropped);
|
orders.skip().send_msg(Msg::ModalDropped);
|
||||||
@ -67,14 +172,6 @@ pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orde
|
|||||||
modal.description = Some(value.clone());
|
modal.description = Some(value.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// IssueTypeAddIssueModal
|
|
||||||
Msg::StyledSelectChanged(
|
|
||||||
FieldId::AddIssueModal(IssueFieldId::Type),
|
|
||||||
StyledSelectChange::Changed(id),
|
|
||||||
) => {
|
|
||||||
modal.issue_type = (*id).into();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReporterAddIssueModal
|
// ReporterAddIssueModal
|
||||||
Msg::StyledSelectChanged(
|
Msg::StyledSelectChanged(
|
||||||
FieldId::AddIssueModal(IssueFieldId::Reporter),
|
FieldId::AddIssueModal(IssueFieldId::Reporter),
|
||||||
@ -120,6 +217,85 @@ pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orde
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
pub fn view(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
||||||
|
let issue_type = modal
|
||||||
|
.type_state
|
||||||
|
.values
|
||||||
|
.get(0)
|
||||||
|
.cloned()
|
||||||
|
.map(|n| Type::from(n))
|
||||||
|
.unwrap_or_else(|| Type::Task);
|
||||||
|
|
||||||
|
let issue_type_field = issue_type_field(modal);
|
||||||
|
let form = StyledForm::build()
|
||||||
|
.heading(issue_type.form_label())
|
||||||
|
.add_field(issue_type_field)
|
||||||
|
.add_field(crate::shared::divider());
|
||||||
|
|
||||||
|
let form = match issue_type {
|
||||||
|
Type::Epic => {
|
||||||
|
let name_field = name_field(modal);
|
||||||
|
form.add_field(name_field)
|
||||||
|
}
|
||||||
|
Type::Task | Type::Story | Type::Bug => {
|
||||||
|
let short_summary_field = short_summary_field(modal);
|
||||||
|
let description_field = description_field();
|
||||||
|
let reporter_field = reporter_field(model, modal);
|
||||||
|
let assignees_field = assignees_field(model, modal);
|
||||||
|
let issue_priority_field = issue_priority_field(modal);
|
||||||
|
let epic_field = epic_field(model, modal);
|
||||||
|
|
||||||
|
form.add_field(short_summary_field)
|
||||||
|
.add_field(description_field)
|
||||||
|
.add_field(reporter_field)
|
||||||
|
.add_field(assignees_field)
|
||||||
|
.add_field(issue_priority_field)
|
||||||
|
.try_field(epic_field)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let submit = {
|
||||||
|
let issue_type = issue_type.clone();
|
||||||
|
StyledButton::build()
|
||||||
|
.primary()
|
||||||
|
.text(issue_type.submit_label())
|
||||||
|
.add_class("action")
|
||||||
|
.add_class("submit")
|
||||||
|
.add_class("actionButton")
|
||||||
|
.on_click(mouse_ev(Ev::Click, move |ev| {
|
||||||
|
ev.stop_propagation();
|
||||||
|
ev.prevent_default();
|
||||||
|
Some(issue_type.submit_action())
|
||||||
|
}))
|
||||||
|
.build()
|
||||||
|
.into_node()
|
||||||
|
};
|
||||||
|
let cancel = StyledButton::build()
|
||||||
|
.empty()
|
||||||
|
.add_class("action")
|
||||||
|
.add_class("cancel")
|
||||||
|
.add_class("actionButton")
|
||||||
|
.text("Cancel")
|
||||||
|
.on_click(mouse_ev(Ev::Click, |ev| {
|
||||||
|
ev.stop_propagation();
|
||||||
|
ev.prevent_default();
|
||||||
|
Some(Msg::ModalDropped)
|
||||||
|
}))
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
let actions = div![attrs![At::Class => "actions"], submit, cancel];
|
||||||
|
|
||||||
|
let form = form.add_field(actions).build().into_node();
|
||||||
|
|
||||||
|
StyledModal::build()
|
||||||
|
.width(800)
|
||||||
|
.add_class("add-issue")
|
||||||
|
.variant(ModalVariant::Center)
|
||||||
|
.children(vec![form])
|
||||||
|
.build()
|
||||||
|
.into_node()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue_type_field(modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let select_type = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Type))
|
let select_type = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Type))
|
||||||
.name("type")
|
.name("type")
|
||||||
.normal()
|
.normal()
|
||||||
@ -127,44 +303,54 @@ pub fn view(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
.opened(modal.type_state.opened)
|
.opened(modal.type_state.opened)
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.options(
|
.options(
|
||||||
IssueType::ordered()
|
Type::ordered()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| t.to_child().name("type"))
|
.map(|t| t.to_child().name("type"))
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.selected(vec![modal.issue_type.to_child().name("type")])
|
.selected(vec![Type::from(
|
||||||
|
modal.type_state.values.get(0).cloned().unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.to_child()
|
||||||
|
.name("type")])
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let issue_type_field = StyledField::build()
|
StyledField::build()
|
||||||
.label("Issue Type")
|
.label("Issue Type")
|
||||||
.tip("Start typing to get a list of possible matches.")
|
.tip("Start typing to get a list of possible matches.")
|
||||||
.input(select_type)
|
.input(select_type)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_summary_field(modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let short_summary = StyledInput::build(FieldId::AddIssueModal(IssueFieldId::Title))
|
let short_summary = StyledInput::build(FieldId::AddIssueModal(IssueFieldId::Title))
|
||||||
.state(&modal.title_state)
|
.state(&modal.title_state)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let short_summary_field = StyledField::build()
|
StyledField::build()
|
||||||
.label("Short Summary")
|
.label("Short Summary")
|
||||||
.tip("Concisely summarize the issue in one or two sentences.")
|
.tip("Concisely summarize the issue in one or two sentences.")
|
||||||
.input(short_summary)
|
.input(short_summary)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description_field() -> Node<Msg> {
|
||||||
let description = StyledTextarea::build(FieldId::AddIssueModal(IssueFieldId::Description))
|
let description = StyledTextarea::build(FieldId::AddIssueModal(IssueFieldId::Description))
|
||||||
.height(110)
|
.height(110)
|
||||||
.add_class("textarea")
|
.add_class("textarea")
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let description_field = StyledField::build()
|
StyledField::build()
|
||||||
.label("Description")
|
.label("Description")
|
||||||
.tip("Describe the issue in as much detail as you'd like.")
|
.tip("Describe the issue in as much detail as you'd like.")
|
||||||
.input(description)
|
.input(description)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reporter_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let reporter_id = modal
|
let reporter_id = modal
|
||||||
.reporter_id
|
.reporter_id
|
||||||
.or_else(|| model.user.as_ref().map(|u| u.id))
|
.or_else(|| model.user.as_ref().map(|u| u.id))
|
||||||
@ -196,13 +382,15 @@ pub fn view(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
.valid(true)
|
.valid(true)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let reporter_field = StyledField::build()
|
StyledField::build()
|
||||||
.input(reporter)
|
.input(reporter)
|
||||||
.label("Reporter")
|
.label("Reporter")
|
||||||
.tip("")
|
.tip("")
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assignees_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let assignees = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Assignees))
|
let assignees = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Assignees))
|
||||||
.normal()
|
.normal()
|
||||||
.multi()
|
.multi()
|
||||||
@ -231,13 +419,15 @@ pub fn view(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
.valid(true)
|
.valid(true)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let assignees_field = StyledField::build()
|
StyledField::build()
|
||||||
.input(assignees)
|
.input(assignees)
|
||||||
.label("Assignees")
|
.label("Assignees")
|
||||||
.tip("")
|
.tip("")
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue_priority_field(modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let select_priority = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Priority))
|
let select_priority = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Priority))
|
||||||
.name("priority")
|
.name("priority")
|
||||||
.normal()
|
.normal()
|
||||||
@ -253,59 +443,55 @@ pub fn view(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
.selected(vec![modal.priority.to_child().name("priority")])
|
.selected(vec![modal.priority.to_child().name("priority")])
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let issue_priority_field = StyledField::build()
|
StyledField::build()
|
||||||
.label("Issue Type")
|
.label("Issue Type")
|
||||||
.tip("Priority in relation to other issues.")
|
.tip("Priority in relation to other issues.")
|
||||||
.input(select_priority)
|
.input(select_priority)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node()
|
||||||
|
}
|
||||||
|
|
||||||
let submit = StyledButton::build()
|
fn epic_field(model: &Model, modal: &AddIssueModal) -> Option<Node<Msg>> {
|
||||||
.primary()
|
if model.epics.is_empty() {
|
||||||
.text("Create Issue")
|
None
|
||||||
.add_class("action")
|
} else {
|
||||||
.add_class("submit")
|
let selected = modal
|
||||||
.add_class("ActionButton")
|
.epic_state
|
||||||
.on_click(mouse_ev(Ev::Click, |ev| {
|
.values
|
||||||
ev.stop_propagation();
|
.get(0)
|
||||||
ev.prevent_default();
|
.and_then(|id| model.epics.iter().find(|epic| epic.id == *id as EpicId))
|
||||||
Some(Msg::AddIssue)
|
.map(|epic| vec![epic.to_child()])
|
||||||
}))
|
.unwrap_or_default();
|
||||||
|
let input = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Epic))
|
||||||
|
.name("epic")
|
||||||
|
.selected(selected)
|
||||||
|
.options(model.epics.iter().map(|epic| epic.to_child()).collect())
|
||||||
|
.normal()
|
||||||
|
.text_filter(modal.epic_state.text_filter.as_str())
|
||||||
|
.opened(modal.epic_state.opened)
|
||||||
|
.valid(true)
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
Some(
|
||||||
|
StyledField::build()
|
||||||
|
.label("Epic")
|
||||||
|
.tip("Feature group")
|
||||||
|
.input(input)
|
||||||
|
.build()
|
||||||
|
.into_node(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name_field(modal: &AddIssueModal) -> Node<Msg> {
|
||||||
|
let name = StyledInput::build(FieldId::AddIssueModal(IssueFieldId::Title))
|
||||||
|
.state(&modal.title_state)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let cancel = StyledButton::build()
|
StyledField::build()
|
||||||
.empty()
|
.label("Epic name")
|
||||||
.add_class("action")
|
.tip("Describe upcoming feature.")
|
||||||
.add_class("cancel")
|
.input(name)
|
||||||
.add_class("actionButton")
|
|
||||||
.text("Cancel")
|
|
||||||
.on_click(mouse_ev(Ev::Click, |ev| {
|
|
||||||
ev.stop_propagation();
|
|
||||||
ev.prevent_default();
|
|
||||||
Some(Msg::ModalDropped)
|
|
||||||
}))
|
|
||||||
.build()
|
|
||||||
.into_node();
|
|
||||||
let actions = div![attrs![At::Class => "actions"], submit, cancel];
|
|
||||||
|
|
||||||
let form = StyledForm::build()
|
|
||||||
.heading("Create issue")
|
|
||||||
.add_field(issue_type_field)
|
|
||||||
.add_field(crate::shared::divider())
|
|
||||||
.add_field(short_summary_field)
|
|
||||||
.add_field(description_field)
|
|
||||||
.add_field(reporter_field)
|
|
||||||
.add_field(assignees_field)
|
|
||||||
.add_field(issue_priority_field)
|
|
||||||
.add_field(actions)
|
|
||||||
.build()
|
|
||||||
.into_node();
|
|
||||||
|
|
||||||
StyledModal::build()
|
|
||||||
.width(800)
|
|
||||||
.add_class("add-issue")
|
|
||||||
.variant(ModalVariant::Center)
|
|
||||||
.children(vec![form])
|
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.issue_type = (*value).into();
|
modal.payload.issue_type = (*value).into();
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Type,
|
IssueFieldId::Type,
|
||||||
PayloadVariant::IssueType(modal.payload.issue_type),
|
PayloadVariant::IssueType(modal.payload.issue_type),
|
||||||
@ -59,7 +59,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.issue_status_id = *value as IssueStatusId;
|
modal.payload.issue_status_id = *value as IssueStatusId;
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::IssueStatusId,
|
IssueFieldId::IssueStatusId,
|
||||||
PayloadVariant::I32(modal.payload.issue_status_id),
|
PayloadVariant::I32(modal.payload.issue_status_id),
|
||||||
@ -74,7 +74,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.reporter_id = *value as i32;
|
modal.payload.reporter_id = *value as i32;
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Reporter,
|
IssueFieldId::Reporter,
|
||||||
PayloadVariant::I32(modal.payload.reporter_id),
|
PayloadVariant::I32(modal.payload.reporter_id),
|
||||||
@ -89,7 +89,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.user_ids.push(*value as i32);
|
modal.payload.user_ids.push(*value as i32);
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Assignees,
|
IssueFieldId::Assignees,
|
||||||
PayloadVariant::VecI32(modal.payload.user_ids.clone()),
|
PayloadVariant::VecI32(modal.payload.user_ids.clone()),
|
||||||
@ -111,7 +111,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Assignees,
|
IssueFieldId::Assignees,
|
||||||
PayloadVariant::VecI32(modal.payload.user_ids.clone()),
|
PayloadVariant::VecI32(modal.payload.user_ids.clone()),
|
||||||
@ -126,7 +126,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.priority = (*value).into();
|
modal.payload.priority = (*value).into();
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Priority,
|
IssueFieldId::Priority,
|
||||||
PayloadVariant::IssuePriority(modal.payload.priority),
|
PayloadVariant::IssuePriority(modal.payload.priority),
|
||||||
@ -141,7 +141,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.title = value.clone();
|
modal.payload.title = value.clone();
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Title,
|
IssueFieldId::Title,
|
||||||
PayloadVariant::String(modal.payload.title.clone()),
|
PayloadVariant::String(modal.payload.title.clone()),
|
||||||
@ -157,7 +157,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
modal.payload.description = Some(value.clone());
|
modal.payload.description = Some(value.clone());
|
||||||
modal.payload.description_text = Some(value.clone());
|
modal.payload.description_text = Some(value.clone());
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Description,
|
IssueFieldId::Description,
|
||||||
PayloadVariant::String(
|
PayloadVariant::String(
|
||||||
@ -180,7 +180,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.time_spent = modal.time_spent.represent_f64_as_i32();
|
modal.payload.time_spent = modal.time_spent.represent_f64_as_i32();
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::TimeSpent,
|
IssueFieldId::TimeSpent,
|
||||||
PayloadVariant::OptionI32(modal.payload.time_spent),
|
PayloadVariant::OptionI32(modal.payload.time_spent),
|
||||||
@ -195,7 +195,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.time_spent = modal.time_spent_select.values.get(0).map(|n| *n as i32);
|
modal.payload.time_spent = modal.time_spent_select.values.get(0).map(|n| *n as i32);
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::TimeSpent,
|
IssueFieldId::TimeSpent,
|
||||||
PayloadVariant::OptionI32(modal.payload.time_spent),
|
PayloadVariant::OptionI32(modal.payload.time_spent),
|
||||||
@ -211,7 +211,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.time_remaining = modal.time_remaining.represent_f64_as_i32();
|
modal.payload.time_remaining = modal.time_remaining.represent_f64_as_i32();
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::TimeRemaining,
|
IssueFieldId::TimeRemaining,
|
||||||
PayloadVariant::OptionI32(modal.payload.time_remaining),
|
PayloadVariant::OptionI32(modal.payload.time_remaining),
|
||||||
@ -227,7 +227,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
modal.payload.time_remaining =
|
modal.payload.time_remaining =
|
||||||
modal.time_remaining_select.values.get(0).map(|n| *n as i32);
|
modal.time_remaining_select.values.get(0).map(|n| *n as i32);
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::TimeRemaining,
|
IssueFieldId::TimeRemaining,
|
||||||
PayloadVariant::OptionI32(modal.payload.time_remaining),
|
PayloadVariant::OptionI32(modal.payload.time_remaining),
|
||||||
@ -243,7 +243,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.estimate = modal.estimate.represent_f64_as_i32();
|
modal.payload.estimate = modal.estimate.represent_f64_as_i32();
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Estimate,
|
IssueFieldId::Estimate,
|
||||||
PayloadVariant::OptionI32(modal.payload.estimate),
|
PayloadVariant::OptionI32(modal.payload.estimate),
|
||||||
@ -258,7 +258,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
) => {
|
) => {
|
||||||
modal.payload.estimate = modal.estimate_select.values.get(0).map(|n| *n as i32);
|
modal.payload.estimate = modal.estimate_select.values.get(0).map(|n| *n as i32);
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Estimate,
|
IssueFieldId::Estimate,
|
||||||
PayloadVariant::OptionI32(modal.payload.estimate),
|
PayloadVariant::OptionI32(modal.payload.estimate),
|
||||||
@ -294,11 +294,11 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
Msg::SaveComment => {
|
Msg::SaveComment => {
|
||||||
let msg = match modal.comment_form.id {
|
let msg = match modal.comment_form.id {
|
||||||
Some(id) => WsMsg::UpdateComment(UpdateCommentPayload {
|
Some(id) => WsMsg::CommentUpdate(UpdateCommentPayload {
|
||||||
id,
|
id,
|
||||||
body: modal.comment_form.body.clone(),
|
body: modal.comment_form.body.clone(),
|
||||||
}),
|
}),
|
||||||
_ => WsMsg::CreateComment(CreateCommentPayload {
|
_ => WsMsg::CommentCreate(CreateCommentPayload {
|
||||||
user_id: None,
|
user_id: None,
|
||||||
body: modal.comment_form.body.clone(),
|
body: modal.comment_form.body.clone(),
|
||||||
issue_id: modal.id,
|
issue_id: modal.id,
|
||||||
@ -328,11 +328,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
modal.comment_form.creating = true;
|
modal.comment_form.creating = true;
|
||||||
}
|
}
|
||||||
Msg::DeleteComment(comment_id) => {
|
Msg::DeleteComment(comment_id) => {
|
||||||
send_ws_msg(
|
send_ws_msg(WsMsg::CommentDelete(*comment_id), model.ws.as_ref(), orders);
|
||||||
WsMsg::CommentDeleteRequest(*comment_id),
|
|
||||||
model.ws.as_ref(),
|
|
||||||
orders,
|
|
||||||
);
|
|
||||||
orders.skip().send_msg(Msg::ModalDropped);
|
orders.skip().send_msg(Msg::ModalDropped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ fn push_edit_modal(issue_id: i32, model: &mut Model, orders: &mut impl Orders<Ms
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueCommentsRequest(issue_id),
|
WsMsg::IssueCommentsLoad(issue_id),
|
||||||
model.ws.as_ref(),
|
model.ws.as_ref(),
|
||||||
orders,
|
orders,
|
||||||
);
|
);
|
||||||
|
@ -154,7 +154,6 @@ impl EditIssueModal {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
||||||
pub struct AddIssueModal {
|
pub struct AddIssueModal {
|
||||||
pub issue_type: IssueType,
|
|
||||||
pub priority: IssuePriority,
|
pub priority: IssuePriority,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub description_text: Option<String>,
|
pub description_text: Option<String>,
|
||||||
@ -173,12 +172,12 @@ pub struct AddIssueModal {
|
|||||||
pub reporter_state: StyledSelectState,
|
pub reporter_state: StyledSelectState,
|
||||||
pub assignees_state: StyledSelectState,
|
pub assignees_state: StyledSelectState,
|
||||||
pub priority_state: StyledSelectState,
|
pub priority_state: StyledSelectState,
|
||||||
|
pub epic_state: StyledSelectState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AddIssueModal {
|
impl Default for AddIssueModal {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
issue_type: Default::default(),
|
|
||||||
priority: Default::default(),
|
priority: Default::default(),
|
||||||
description: Default::default(),
|
description: Default::default(),
|
||||||
description_text: Default::default(),
|
description_text: Default::default(),
|
||||||
@ -204,6 +203,7 @@ impl Default for AddIssueModal {
|
|||||||
FieldId::AddIssueModal(IssueFieldId::Priority),
|
FieldId::AddIssueModal(IssueFieldId::Priority),
|
||||||
vec![],
|
vec![],
|
||||||
),
|
),
|
||||||
|
epic_state: StyledSelectState::new(FieldId::AddIssueModal(IssueFieldId::Epic), vec![]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -509,6 +509,7 @@ pub struct Model {
|
|||||||
pub messages: Vec<Message>,
|
pub messages: Vec<Message>,
|
||||||
pub user_projects: Vec<UserProject>,
|
pub user_projects: Vec<UserProject>,
|
||||||
pub projects: Vec<Project>,
|
pub projects: Vec<Project>,
|
||||||
|
pub epics: Vec<Epic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
@ -538,6 +539,7 @@ impl Model {
|
|||||||
messages: vec![],
|
messages: vec![],
|
||||||
user_projects: vec![],
|
user_projects: vec![],
|
||||||
projects: vec![],
|
projects: vec![],
|
||||||
|
epics: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,14 +5,14 @@ use jirs_data::{UsersFieldId, WsMsg};
|
|||||||
|
|
||||||
use crate::model::{Model, Page, PageContent, ProfilePage};
|
use crate::model::{Model, Page, PageContent, ProfilePage};
|
||||||
use crate::shared::styled_select::StyledSelectChange;
|
use crate::shared::styled_select::StyledSelectChange;
|
||||||
use crate::ws::{enqueue_ws_msg, send_ws_msg};
|
use crate::ws::{board_load, send_ws_msg};
|
||||||
use crate::{FieldId, Msg, PageChanged, ProfilePageChange, WebSocketChanged};
|
use crate::{FieldId, Msg, PageChanged, ProfilePageChange, WebSocketChanged};
|
||||||
|
|
||||||
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
||||||
match msg {
|
match msg {
|
||||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)))
|
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)))
|
||||||
| Msg::ChangePage(Page::Profile) => {
|
| Msg::ChangePage(Page::Profile) => {
|
||||||
init_load(model, orders);
|
board_load(model, orders);
|
||||||
build_page_content(model);
|
build_page_content(model);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -87,13 +87,6 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_load(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|
||||||
if model.user.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
enqueue_ws_msg(vec![WsMsg::ProjectIssuesRequest], model.ws.as_ref(), orders);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_page_content(model: &mut Model) {
|
fn build_page_content(model: &mut Model) {
|
||||||
let user = match model.user {
|
let user = match model.user {
|
||||||
Some(ref user) => user,
|
Some(ref user) => user,
|
||||||
|
@ -4,7 +4,7 @@ use jirs_data::{Issue, IssueFieldId, WsMsg};
|
|||||||
|
|
||||||
use crate::model::{ModalType, Model, Page, PageContent, ProjectPage};
|
use crate::model::{ModalType, Model, Page, PageContent, ProjectPage};
|
||||||
use crate::shared::styled_select::StyledSelectChange;
|
use crate::shared::styled_select::StyledSelectChange;
|
||||||
use crate::ws::{enqueue_ws_msg, send_ws_msg};
|
use crate::ws::{board_load, send_ws_msg};
|
||||||
use crate::{BoardPageChange, EditIssueModalSection, FieldId, Msg, PageChanged, WebSocketChanged};
|
use crate::{BoardPageChange, EditIssueModalSection, FieldId, Msg, PageChanged, WebSocketChanged};
|
||||||
|
|
||||||
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
||||||
@ -32,7 +32,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
| Msg::ChangePage(Page::Project)
|
| Msg::ChangePage(Page::Project)
|
||||||
| Msg::ChangePage(Page::AddIssue)
|
| Msg::ChangePage(Page::AddIssue)
|
||||||
| Msg::ChangePage(Page::EditIssue(..)) => {
|
| Msg::ChangePage(Page::EditIssue(..)) => {
|
||||||
init_load(model, orders);
|
board_load(model, orders);
|
||||||
}
|
}
|
||||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueUpdated(issue))) => {
|
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueUpdated(issue))) => {
|
||||||
let mut old: Vec<Issue> = vec![];
|
let mut old: Vec<Issue> = vec![];
|
||||||
@ -116,7 +116,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
}
|
}
|
||||||
Msg::DeleteIssue(issue_id) => {
|
Msg::DeleteIssue(issue_id) => {
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
jirs_data::WsMsg::IssueDeleteRequest(issue_id),
|
jirs_data::WsMsg::IssueDelete(issue_id),
|
||||||
model.ws.as_ref(),
|
model.ws.as_ref(),
|
||||||
orders,
|
orders,
|
||||||
);
|
);
|
||||||
@ -125,14 +125,6 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_load(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|
||||||
enqueue_ws_msg(
|
|
||||||
vec![WsMsg::ProjectIssuesRequest, WsMsg::IssueStatusesRequest],
|
|
||||||
model.ws.as_ref(),
|
|
||||||
orders,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_page_content(model: &mut Model) {
|
fn build_page_content(model: &mut Model) {
|
||||||
model.page_content = PageContent::Project(Box::new(ProjectPage::default()));
|
model.page_content = PageContent::Project(Box::new(ProjectPage::default()));
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use jirs_data::{IssueStatus, IssueStatusId, ProjectFieldId, UpdateProjectPayload
|
|||||||
|
|
||||||
use crate::model::{Model, Page, PageContent, ProjectSettingsPage};
|
use crate::model::{Model, Page, PageContent, ProjectSettingsPage};
|
||||||
use crate::shared::styled_select::StyledSelectChange;
|
use crate::shared::styled_select::StyledSelectChange;
|
||||||
use crate::ws::{enqueue_ws_msg, send_ws_msg};
|
use crate::ws::{board_load, send_ws_msg};
|
||||||
use crate::FieldChange::TabChanged;
|
use crate::FieldChange::TabChanged;
|
||||||
use crate::{FieldId, Msg, PageChanged, ProjectPageChange, WebSocketChanged};
|
use crate::{FieldId, Msg, PageChanged, ProjectPageChange, WebSocketChanged};
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
Msg::WebSocketChange(ref change) => match change {
|
Msg::WebSocketChange(ref change) => match change {
|
||||||
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)) => {
|
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)) => {
|
||||||
init_load(model, orders);
|
board_load(model, orders);
|
||||||
}
|
}
|
||||||
WebSocketChanged::WsMsg(WsMsg::IssueStatusCreated(_)) => {
|
WebSocketChanged::WsMsg(WsMsg::IssueStatusCreated(_)) => {
|
||||||
match &mut model.page_content {
|
match &mut model.page_content {
|
||||||
@ -37,7 +37,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
Msg::ChangePage(Page::ProjectSettings) => {
|
Msg::ChangePage(Page::ProjectSettings) => {
|
||||||
build_page_content(model);
|
build_page_content(model);
|
||||||
if model.user.is_some() {
|
if model.user.is_some() {
|
||||||
init_load(model, orders);
|
board_load(model, orders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -83,7 +83,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
ProjectPageChange::SubmitProjectSettingsForm,
|
ProjectPageChange::SubmitProjectSettingsForm,
|
||||||
)) => {
|
)) => {
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::ProjectUpdateRequest(UpdateProjectPayload {
|
WsMsg::ProjectUpdateLoad(UpdateProjectPayload {
|
||||||
id: page.payload.id,
|
id: page.payload.id,
|
||||||
name: page.payload.name.clone(),
|
name: page.payload.name.clone(),
|
||||||
url: page.payload.url.clone(),
|
url: page.payload.url.clone(),
|
||||||
@ -160,14 +160,6 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_load(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|
||||||
enqueue_ws_msg(
|
|
||||||
vec![WsMsg::IssueStatusesRequest, WsMsg::ProjectIssuesRequest],
|
|
||||||
model.ws.as_ref(),
|
|
||||||
orders,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) {
|
fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) {
|
||||||
let page = match &mut model.page_content {
|
let page = match &mut model.page_content {
|
||||||
PageContent::ProjectSettings(page) => page,
|
PageContent::ProjectSettings(page) => page,
|
||||||
|
@ -4,7 +4,7 @@ use jirs_data::WsMsg;
|
|||||||
|
|
||||||
use crate::changes::{PageChanged, ReportsPageChange};
|
use crate::changes::{PageChanged, ReportsPageChange};
|
||||||
use crate::model::{Model, Page, PageContent, ReportsPage};
|
use crate::model::{Model, Page, PageContent, ReportsPage};
|
||||||
use crate::ws::enqueue_ws_msg;
|
use crate::ws::board_load;
|
||||||
use crate::{Msg, WebSocketChanged};
|
use crate::{Msg, WebSocketChanged};
|
||||||
|
|
||||||
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
||||||
@ -24,7 +24,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
Msg::UserChanged(Some(..))
|
Msg::UserChanged(Some(..))
|
||||||
| Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)))
|
| Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)))
|
||||||
| Msg::ChangePage(Page::Reports) => {
|
| Msg::ChangePage(Page::Reports) => {
|
||||||
init_load(model, orders);
|
board_load(model, orders);
|
||||||
}
|
}
|
||||||
Msg::PageChanged(PageChanged::Reports(ReportsPageChange::DayHovered(v))) => {
|
Msg::PageChanged(PageChanged::Reports(ReportsPageChange::DayHovered(v))) => {
|
||||||
page.hovered_day = v;
|
page.hovered_day = v;
|
||||||
@ -39,15 +39,3 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
fn build_page_content(model: &mut Model) {
|
fn build_page_content(model: &mut Model) {
|
||||||
model.page_content = PageContent::Reports(Box::new(ReportsPage::default()))
|
model.page_content = PageContent::Reports(Box::new(ReportsPage::default()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_load(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|
||||||
if model.user.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
enqueue_ws_msg(
|
|
||||||
vec![WsMsg::ProjectIssuesRequest, WsMsg::IssueStatusesRequest],
|
|
||||||
model.ws.as_ref(),
|
|
||||||
orders,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
@ -14,8 +14,8 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
vec![
|
vec![
|
||||||
WsMsg::UserProjectsLoad,
|
WsMsg::UserProjectsLoad,
|
||||||
WsMsg::ProjectsLoad,
|
WsMsg::ProjectsLoad,
|
||||||
WsMsg::MessagesRequest,
|
WsMsg::MessagesLoad,
|
||||||
WsMsg::ProjectUsersRequest,
|
WsMsg::ProjectUsersLoad,
|
||||||
],
|
],
|
||||||
model.ws.as_ref(),
|
model.ws.as_ref(),
|
||||||
orders,
|
orders,
|
||||||
|
@ -35,6 +35,13 @@ impl StyledFormBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_field(mut self, node: Option<Node<Msg>>) -> Self {
|
||||||
|
if let Some(n) = node {
|
||||||
|
self.fields.push(n);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn heading<S>(mut self, heading: S) -> Self
|
pub fn heading<S>(mut self, heading: S) -> Self
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
|
@ -23,6 +23,7 @@ impl ToString for Variant {
|
|||||||
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
||||||
pub struct StyledInputState {
|
pub struct StyledInputState {
|
||||||
id: FieldId,
|
id: FieldId,
|
||||||
|
touched: bool,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ impl StyledInputState {
|
|||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
|
touched: false,
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,6 +55,7 @@ impl StyledInputState {
|
|||||||
match msg {
|
match msg {
|
||||||
Msg::StrInputChanged(field_id, s) if field_id == &self.id => {
|
Msg::StrInputChanged(field_id, s) if field_id == &self.id => {
|
||||||
self.value = s.clone();
|
self.value = s.clone();
|
||||||
|
self.touched = true;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -129,7 +132,7 @@ impl StyledInputBuilder {
|
|||||||
|
|
||||||
pub fn state(self, state: &StyledInputState) -> Self {
|
pub fn state(self, state: &StyledInputState) -> Self {
|
||||||
self.value(state.value.as_str())
|
self.value(state.value.as_str())
|
||||||
.valid(!state.value.is_empty())
|
.valid(!state.touched || !state.value.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_input_class<S>(mut self, name: S) -> Self
|
pub fn add_input_class<S>(mut self, name: S) -> Self
|
||||||
|
@ -279,6 +279,16 @@ impl ToChild for jirs_data::Project {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToChild for jirs_data::Epic {
|
||||||
|
type Builder = StyledSelectChildBuilder;
|
||||||
|
|
||||||
|
fn to_child(&self) -> Self::Builder {
|
||||||
|
StyledSelectChild::build()
|
||||||
|
.text(self.name.as_str())
|
||||||
|
.value(self.id as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToChild for u32 {
|
impl ToChild for u32 {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder;
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use jirs_data::{InvitationState, UserRole, UsersFieldId, WsMsg};
|
|||||||
|
|
||||||
use crate::model::{InvitationFormState, Model, Page, PageContent, UsersPage};
|
use crate::model::{InvitationFormState, Model, Page, PageContent, UsersPage};
|
||||||
use crate::shared::styled_select::StyledSelectChange;
|
use crate::shared::styled_select::StyledSelectChange;
|
||||||
use crate::ws::{enqueue_ws_msg, send_ws_msg};
|
use crate::ws::{invitation_load, send_ws_msg};
|
||||||
use crate::{FieldId, Msg, PageChanged, UsersPageChange, WebSocketChanged};
|
use crate::{FieldId, Msg, PageChanged, UsersPageChange, WebSocketChanged};
|
||||||
|
|
||||||
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||||
@ -22,11 +22,11 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
Msg::ChangePage(Page::Users) if model.user.is_some() => {
|
Msg::ChangePage(Page::Users) if model.user.is_some() => {
|
||||||
init_load(model, orders);
|
invitation_load(model, orders);
|
||||||
}
|
}
|
||||||
Msg::WebSocketChange(change) => match change {
|
Msg::WebSocketChange(change) => match change {
|
||||||
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(Ok(_))) if model.user.is_some() => {
|
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(Ok(_))) if model.user.is_some() => {
|
||||||
init_load(model, orders);
|
invitation_load(model, orders);
|
||||||
}
|
}
|
||||||
WebSocketChanged::WsMsg(WsMsg::InvitedUsersLoaded(users)) => {
|
WebSocketChanged::WsMsg(WsMsg::InvitedUsersLoaded(users)) => {
|
||||||
page.invited_users = users;
|
page.invited_users = users;
|
||||||
@ -43,7 +43,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
page.invitations.push(invitation);
|
page.invitations.push(invitation);
|
||||||
}
|
}
|
||||||
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref(), orders);
|
send_ws_msg(WsMsg::InvitationListLoad, model.ws.as_ref(), orders);
|
||||||
}
|
}
|
||||||
WebSocketChanged::WsMsg(WsMsg::InvitedUserRemoveSuccess(removed_id)) => {
|
WebSocketChanged::WsMsg(WsMsg::InvitedUserRemoveSuccess(removed_id)) => {
|
||||||
let mut old = vec![];
|
let mut old = vec![];
|
||||||
@ -55,7 +55,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
WebSocketChanged::WsMsg(WsMsg::InvitationSendSuccess) => {
|
WebSocketChanged::WsMsg(WsMsg::InvitationSendSuccess) => {
|
||||||
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref(), orders);
|
send_ws_msg(WsMsg::InvitationListLoad, model.ws.as_ref(), orders);
|
||||||
page.form_state = InvitationFormState::Succeed;
|
page.form_state = InvitationFormState::Succeed;
|
||||||
}
|
}
|
||||||
WebSocketChanged::WsMsg(WsMsg::InvitationSendFailure) => {
|
WebSocketChanged::WsMsg(WsMsg::InvitationSendFailure) => {
|
||||||
@ -124,11 +124,3 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
fn build_page_content(model: &mut Model) {
|
fn build_page_content(model: &mut Model) {
|
||||||
model.page_content = PageContent::Users(Box::new(UsersPage::default()));
|
model.page_content = PageContent::Users(Box::new(UsersPage::default()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_load(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|
||||||
enqueue_ws_msg(
|
|
||||||
vec![WsMsg::InvitationListRequest, WsMsg::InvitedUsersRequest],
|
|
||||||
model.ws.as_ref(),
|
|
||||||
orders,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
27
jirs-client/src/ws/init_load_sets.rs
Normal file
27
jirs-client/src/ws/init_load_sets.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use seed::app::Orders;
|
||||||
|
|
||||||
|
use jirs_data::WsMsg;
|
||||||
|
|
||||||
|
use crate::model::Model;
|
||||||
|
use crate::ws::enqueue_ws_msg;
|
||||||
|
use crate::Msg;
|
||||||
|
|
||||||
|
pub fn board_load(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||||
|
enqueue_ws_msg(
|
||||||
|
vec![
|
||||||
|
WsMsg::IssueStatusesLoad,
|
||||||
|
WsMsg::ProjectIssuesLoad,
|
||||||
|
WsMsg::EpicsLoad,
|
||||||
|
],
|
||||||
|
model.ws.as_ref(),
|
||||||
|
orders,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invitation_load(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||||
|
enqueue_ws_msg(
|
||||||
|
vec![WsMsg::InvitationListLoad, WsMsg::InvitedUsersLoad],
|
||||||
|
model.ws.as_ref(),
|
||||||
|
orders,
|
||||||
|
);
|
||||||
|
}
|
@ -93,7 +93,7 @@ pub fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
issue.id,
|
issue.id,
|
||||||
IssueFieldId::IssueStatusId,
|
IssueFieldId::IssueStatusId,
|
||||||
PayloadVariant::I32(issue.issue_status_id),
|
PayloadVariant::I32(issue.issue_status_id),
|
||||||
@ -102,7 +102,7 @@ pub fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
orders,
|
orders,
|
||||||
);
|
);
|
||||||
send_ws_msg(
|
send_ws_msg(
|
||||||
WsMsg::IssueUpdateRequest(
|
WsMsg::IssueUpdate(
|
||||||
issue.id,
|
issue.id,
|
||||||
IssueFieldId::ListPosition,
|
IssueFieldId::ListPosition,
|
||||||
PayloadVariant::I32(issue.list_position),
|
PayloadVariant::I32(issue.list_position),
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
use seed::prelude::*;
|
use seed::prelude::*;
|
||||||
|
|
||||||
|
pub use init_load_sets::*;
|
||||||
use jirs_data::WsMsg;
|
use jirs_data::WsMsg;
|
||||||
|
|
||||||
use crate::model::*;
|
use crate::model::*;
|
||||||
use crate::shared::{go_to_board, write_auth_token};
|
use crate::shared::{go_to_board, write_auth_token};
|
||||||
use crate::{Msg, WebSocketChanged};
|
use crate::{Msg, WebSocketChanged};
|
||||||
|
|
||||||
|
mod init_load_sets;
|
||||||
|
|
||||||
pub mod issue;
|
pub mod issue;
|
||||||
|
|
||||||
pub fn flush_queue(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
pub fn flush_queue(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||||
@ -123,7 +126,7 @@ pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
model.issues = v;
|
model.issues = v;
|
||||||
}
|
}
|
||||||
// issue statuses
|
// issue statuses
|
||||||
WsMsg::IssueStatusesResponse(v) => {
|
WsMsg::IssueStatusesLoaded(v) => {
|
||||||
model.issue_statuses = v.clone();
|
model.issue_statuses = v.clone();
|
||||||
model
|
model
|
||||||
.issue_statuses
|
.issue_statuses
|
||||||
@ -191,6 +194,17 @@ pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
v.sort_by(|a, b| a.updated_at.cmp(&b.updated_at));
|
v.sort_by(|a, b| a.updated_at.cmp(&b.updated_at));
|
||||||
model.comments = v;
|
model.comments = v;
|
||||||
}
|
}
|
||||||
|
WsMsg::CommentUpdated(comment) => {
|
||||||
|
let mut old = vec![];
|
||||||
|
std::mem::swap(&mut model.comments, &mut old);
|
||||||
|
for current in old.into_iter() {
|
||||||
|
if current.id != comment.id {
|
||||||
|
model.comments.push(current);
|
||||||
|
} else {
|
||||||
|
model.comments.push(comment.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
WsMsg::CommentDeleted(comment_id) => {
|
WsMsg::CommentDeleted(comment_id) => {
|
||||||
let mut old = vec![];
|
let mut old = vec![];
|
||||||
std::mem::swap(&mut model.comments, &mut old);
|
std::mem::swap(&mut model.comments, &mut old);
|
||||||
@ -225,7 +239,7 @@ pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
}
|
}
|
||||||
WsMsg::MessagesResponse(v) => {
|
WsMsg::MessagesLoaded(v) => {
|
||||||
model.messages = v.clone();
|
model.messages = v.clone();
|
||||||
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
}
|
}
|
||||||
@ -239,6 +253,37 @@ pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
}
|
}
|
||||||
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// epics
|
||||||
|
WsMsg::EpicsLoaded(epics) => {
|
||||||
|
model.epics = epics.clone();
|
||||||
|
}
|
||||||
|
WsMsg::EpicCreated(epic) => {
|
||||||
|
model.epics.push(epic.clone());
|
||||||
|
model.epics.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
|
}
|
||||||
|
WsMsg::EpicUpdated(epic) => {
|
||||||
|
let mut old = vec![];
|
||||||
|
std::mem::swap(&mut old, &mut model.epics);
|
||||||
|
for current in old {
|
||||||
|
if current.id != epic.id {
|
||||||
|
model.epics.push(current);
|
||||||
|
} else {
|
||||||
|
model.epics.push(epic.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
model.epics.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
|
}
|
||||||
|
WsMsg::EpicDeleted(id) => {
|
||||||
|
let mut old = vec![];
|
||||||
|
std::mem::swap(&mut old, &mut model.epics);
|
||||||
|
for current in old {
|
||||||
|
if current.id != *id {
|
||||||
|
model.epics.push(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
model.epics.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
orders.render();
|
orders.render();
|
||||||
|
59
jirs-data/src/fields.rs
Normal file
59
jirs-data/src/fields.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
|
pub enum ProjectFieldId {
|
||||||
|
Name,
|
||||||
|
Url,
|
||||||
|
Description,
|
||||||
|
Category,
|
||||||
|
TimeTracking,
|
||||||
|
IssueStatusName,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
|
pub enum SignInFieldId {
|
||||||
|
Username,
|
||||||
|
Email,
|
||||||
|
Token,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
|
pub enum SignUpFieldId {
|
||||||
|
Username,
|
||||||
|
Email,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
|
pub enum UsersFieldId {
|
||||||
|
Username,
|
||||||
|
Email,
|
||||||
|
UserRole,
|
||||||
|
Avatar,
|
||||||
|
CurrentProject,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
|
pub enum InviteFieldId {
|
||||||
|
Token,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
|
pub enum CommentFieldId {
|
||||||
|
Body,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
|
pub enum IssueFieldId {
|
||||||
|
Type,
|
||||||
|
Title,
|
||||||
|
Description,
|
||||||
|
ListPosition,
|
||||||
|
Assignees,
|
||||||
|
Reporter,
|
||||||
|
Priority,
|
||||||
|
Estimate,
|
||||||
|
TimeSpent,
|
||||||
|
TimeRemaining,
|
||||||
|
IssueStatusId,
|
||||||
|
Epic,
|
||||||
|
}
|
@ -7,9 +7,16 @@ use diesel::*;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub use fields::*;
|
||||||
|
pub use msg::WsMsg;
|
||||||
|
pub use payloads::*;
|
||||||
#[cfg(feature = "backend")]
|
#[cfg(feature = "backend")]
|
||||||
pub use sql::*;
|
pub use sql::*;
|
||||||
|
|
||||||
|
mod fields;
|
||||||
|
mod msg;
|
||||||
|
mod payloads;
|
||||||
|
|
||||||
#[cfg(feature = "backend")]
|
#[cfg(feature = "backend")]
|
||||||
pub mod sql;
|
pub mod sql;
|
||||||
|
|
||||||
@ -29,9 +36,12 @@ pub type InvitationId = i32;
|
|||||||
pub type Position = i32;
|
pub type Position = i32;
|
||||||
pub type MessageId = i32;
|
pub type MessageId = i32;
|
||||||
pub type EpicId = i32;
|
pub type EpicId = i32;
|
||||||
|
|
||||||
pub type EmailString = String;
|
pub type EmailString = String;
|
||||||
pub type UsernameString = String;
|
pub type UsernameString = String;
|
||||||
pub type TitleString = String;
|
pub type TitleString = String;
|
||||||
|
pub type NameString = String;
|
||||||
|
|
||||||
pub type BindToken = Uuid;
|
pub type BindToken = Uuid;
|
||||||
pub type InvitationToken = Uuid;
|
pub type InvitationToken = Uuid;
|
||||||
|
|
||||||
@ -505,23 +515,6 @@ pub struct Token {
|
|||||||
pub bind_token: Option<Uuid>,
|
pub bind_token: Option<Uuid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, PartialOrd)]
|
|
||||||
pub struct UpdateIssuePayload {
|
|
||||||
pub title: String,
|
|
||||||
pub issue_type: IssueType,
|
|
||||||
pub priority: IssuePriority,
|
|
||||||
pub list_position: i32,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub description_text: Option<String>,
|
|
||||||
pub estimate: Option<i32>,
|
|
||||||
pub time_spent: Option<i32>,
|
|
||||||
pub time_remaining: Option<i32>,
|
|
||||||
pub project_id: ProjectId,
|
|
||||||
pub reporter_id: UserId,
|
|
||||||
pub issue_status_id: IssueStatusId,
|
|
||||||
pub user_ids: Vec<UserId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "backend", derive(Queryable))]
|
#[cfg_attr(feature = "backend", derive(Queryable))]
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct IssueAssignee {
|
pub struct IssueAssignee {
|
||||||
@ -532,26 +525,6 @@ pub struct IssueAssignee {
|
|||||||
pub updated_at: NaiveDateTime,
|
pub updated_at: NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Issue> for UpdateIssuePayload {
|
|
||||||
fn from(issue: Issue) -> Self {
|
|
||||||
Self {
|
|
||||||
title: issue.title,
|
|
||||||
issue_type: issue.issue_type,
|
|
||||||
priority: issue.priority,
|
|
||||||
list_position: issue.list_position,
|
|
||||||
description: issue.description,
|
|
||||||
description_text: issue.description_text,
|
|
||||||
estimate: issue.estimate,
|
|
||||||
time_spent: issue.time_spent,
|
|
||||||
time_remaining: issue.time_remaining,
|
|
||||||
project_id: issue.project_id,
|
|
||||||
reporter_id: issue.reporter_id,
|
|
||||||
user_ids: issue.user_ids,
|
|
||||||
issue_status_id: issue.issue_status_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
|
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
|
||||||
#[cfg_attr(feature = "backend", sql_type = "MessageTypeType")]
|
#[cfg_attr(feature = "backend", sql_type = "MessageTypeType")]
|
||||||
#[derive(Clone, Copy, Deserialize, Serialize, Debug, PartialOrd, PartialEq, Hash)]
|
#[derive(Clone, Copy, Deserialize, Serialize, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
@ -610,222 +583,9 @@ pub struct Message {
|
|||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct Epic {
|
pub struct Epic {
|
||||||
pub id: EpicId,
|
pub id: EpicId,
|
||||||
pub name: String,
|
pub name: NameString,
|
||||||
pub user_id: UserId,
|
pub user_id: UserId,
|
||||||
pub project_id: ProjectId,
|
pub project_id: ProjectId,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub updated_at: NaiveDateTime,
|
pub updated_at: NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct CreateCommentPayload {
|
|
||||||
pub user_id: Option<UserId>,
|
|
||||||
pub issue_id: IssueId,
|
|
||||||
pub body: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct UpdateCommentPayload {
|
|
||||||
pub id: CommentId,
|
|
||||||
pub body: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
|
||||||
pub struct CreateIssuePayload {
|
|
||||||
pub title: String,
|
|
||||||
pub issue_type: IssueType,
|
|
||||||
pub priority: IssuePriority,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub description_text: Option<String>,
|
|
||||||
pub estimate: Option<i32>,
|
|
||||||
pub time_spent: Option<i32>,
|
|
||||||
pub time_remaining: Option<i32>,
|
|
||||||
pub project_id: ProjectId,
|
|
||||||
pub user_ids: Vec<UserId>,
|
|
||||||
pub reporter_id: UserId,
|
|
||||||
pub issue_status_id: IssueStatusId,
|
|
||||||
pub epic_id: Option<EpicId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
|
||||||
pub struct UpdateProjectPayload {
|
|
||||||
pub id: ProjectId,
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub url: Option<String>,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub category: Option<ProjectCategory>,
|
|
||||||
pub time_tracking: Option<TimeTracking>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
||||||
pub enum PayloadVariant {
|
|
||||||
OptionI32(Option<i32>),
|
|
||||||
VecI32(Vec<i32>),
|
|
||||||
I32(i32),
|
|
||||||
String(String),
|
|
||||||
IssueType(IssueType),
|
|
||||||
IssuePriority(IssuePriority),
|
|
||||||
ProjectCategory(ProjectCategory),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
|
||||||
pub enum ProjectFieldId {
|
|
||||||
Name,
|
|
||||||
Url,
|
|
||||||
Description,
|
|
||||||
Category,
|
|
||||||
TimeTracking,
|
|
||||||
IssueStatusName,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
|
||||||
pub enum SignInFieldId {
|
|
||||||
Username,
|
|
||||||
Email,
|
|
||||||
Token,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
|
||||||
pub enum SignUpFieldId {
|
|
||||||
Username,
|
|
||||||
Email,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
|
||||||
pub enum UsersFieldId {
|
|
||||||
Username,
|
|
||||||
Email,
|
|
||||||
UserRole,
|
|
||||||
Avatar,
|
|
||||||
CurrentProject,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
|
||||||
pub enum InviteFieldId {
|
|
||||||
Token,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
|
||||||
pub enum CommentFieldId {
|
|
||||||
Body,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]
|
|
||||||
pub enum IssueFieldId {
|
|
||||||
Type,
|
|
||||||
Title,
|
|
||||||
Description,
|
|
||||||
ListPosition,
|
|
||||||
Assignees,
|
|
||||||
Reporter,
|
|
||||||
Priority,
|
|
||||||
Estimate,
|
|
||||||
TimeSpent,
|
|
||||||
TimeRemaining,
|
|
||||||
IssueStatusId,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
|
||||||
pub enum WsMsg {
|
|
||||||
Ping,
|
|
||||||
Pong,
|
|
||||||
Die,
|
|
||||||
|
|
||||||
// auth
|
|
||||||
AuthorizeRequest(Uuid),
|
|
||||||
AuthorizeLoaded(Result<User, String>),
|
|
||||||
AuthorizeExpired,
|
|
||||||
AuthenticateRequest(EmailString, UsernameString),
|
|
||||||
AuthenticateSuccess,
|
|
||||||
BindTokenCheck(Uuid),
|
|
||||||
BindTokenBad,
|
|
||||||
BindTokenOk(Uuid),
|
|
||||||
|
|
||||||
// Sign up
|
|
||||||
SignUpRequest(EmailString, UsernameString),
|
|
||||||
SignUpSuccess,
|
|
||||||
SignUpPairTaken,
|
|
||||||
|
|
||||||
// invitations
|
|
||||||
InvitationListRequest,
|
|
||||||
InvitationListLoaded(Vec<Invitation>),
|
|
||||||
//
|
|
||||||
InvitedUsersRequest,
|
|
||||||
InvitedUsersLoaded(Vec<User>),
|
|
||||||
//
|
|
||||||
InvitationSendRequest {
|
|
||||||
name: UsernameString,
|
|
||||||
email: EmailString,
|
|
||||||
role: UserRole,
|
|
||||||
},
|
|
||||||
InvitationSendSuccess,
|
|
||||||
InvitationSendFailure,
|
|
||||||
//
|
|
||||||
InvitationRevokeRequest(InvitationId),
|
|
||||||
InvitationRevokeSuccess(InvitationId),
|
|
||||||
//
|
|
||||||
InvitationAcceptRequest(InvitationToken),
|
|
||||||
InvitationAcceptSuccess(BindToken),
|
|
||||||
InvitationAcceptFailure(InvitationToken),
|
|
||||||
//
|
|
||||||
InvitationRejectRequest(InvitationToken),
|
|
||||||
InvitationRejectSuccess,
|
|
||||||
InvitationRejectFailure(InvitationToken),
|
|
||||||
//
|
|
||||||
InvitedUserRemoveRequest(UserId),
|
|
||||||
InvitedUserRemoveSuccess(UserId),
|
|
||||||
|
|
||||||
// project page
|
|
||||||
ProjectsLoad,
|
|
||||||
ProjectsLoaded(Vec<Project>),
|
|
||||||
|
|
||||||
ProjectIssuesRequest,
|
|
||||||
ProjectIssuesLoaded(Vec<Issue>),
|
|
||||||
ProjectUsersRequest,
|
|
||||||
ProjectUsersLoaded(Vec<User>),
|
|
||||||
ProjectUpdateRequest(UpdateProjectPayload),
|
|
||||||
|
|
||||||
// issue
|
|
||||||
IssueUpdateRequest(IssueId, IssueFieldId, PayloadVariant),
|
|
||||||
IssueUpdated(Issue),
|
|
||||||
IssueDeleteRequest(IssueId),
|
|
||||||
IssueDeleted(IssueId),
|
|
||||||
IssueCreateRequest(CreateIssuePayload),
|
|
||||||
IssueCreated(Issue),
|
|
||||||
|
|
||||||
// issue status
|
|
||||||
IssueStatusesRequest,
|
|
||||||
IssueStatusesResponse(Vec<IssueStatus>),
|
|
||||||
IssueStatusUpdate(IssueStatusId, TitleString, Position),
|
|
||||||
IssueStatusUpdated(IssueStatus),
|
|
||||||
IssueStatusCreate(TitleString, Position),
|
|
||||||
IssueStatusCreated(IssueStatus),
|
|
||||||
IssueStatusDelete(IssueStatusId),
|
|
||||||
IssueStatusDeleted(IssueStatusId),
|
|
||||||
|
|
||||||
// comments
|
|
||||||
IssueCommentsRequest(IssueId),
|
|
||||||
IssueCommentsLoaded(Vec<Comment>),
|
|
||||||
CreateComment(CreateCommentPayload),
|
|
||||||
UpdateComment(UpdateCommentPayload),
|
|
||||||
CommentDeleteRequest(CommentId),
|
|
||||||
CommentDeleted(CommentId),
|
|
||||||
|
|
||||||
// users
|
|
||||||
AvatarUrlChanged(UserId, String),
|
|
||||||
ProfileUpdate(EmailString, UsernameString),
|
|
||||||
ProfileUpdated,
|
|
||||||
|
|
||||||
// user projects
|
|
||||||
UserProjectsLoad,
|
|
||||||
UserProjectsLoaded(Vec<UserProject>),
|
|
||||||
UserProjectSetCurrent(UserProjectId),
|
|
||||||
UserProjectCurrentChanged(UserProject),
|
|
||||||
|
|
||||||
// messages
|
|
||||||
Message(Message),
|
|
||||||
MessagesRequest,
|
|
||||||
MessagesResponse(Vec<Message>),
|
|
||||||
MessageMarkSeen(MessageId),
|
|
||||||
MessageMarkedSeen(MessageId),
|
|
||||||
}
|
|
||||||
|
127
jirs-data/src/msg.rs
Normal file
127
jirs-data/src/msg.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
BindToken, Comment, CommentId, CreateCommentPayload, CreateIssuePayload, EmailString, Epic,
|
||||||
|
EpicId, Invitation, InvitationId, InvitationToken, Issue, IssueFieldId, IssueId, IssueStatus,
|
||||||
|
IssueStatusId, Message, MessageId, NameString, PayloadVariant, Position, Project, TitleString,
|
||||||
|
UpdateCommentPayload, UpdateProjectPayload, User, UserId, UserProject, UserProjectId, UserRole,
|
||||||
|
UsernameString,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||||
|
pub enum WsMsg {
|
||||||
|
Ping,
|
||||||
|
Pong,
|
||||||
|
Die,
|
||||||
|
|
||||||
|
// auth
|
||||||
|
AuthorizeLoad(Uuid),
|
||||||
|
AuthorizeLoaded(Result<User, String>),
|
||||||
|
AuthorizeExpired,
|
||||||
|
AuthenticateRequest(EmailString, UsernameString),
|
||||||
|
AuthenticateSuccess,
|
||||||
|
BindTokenCheck(Uuid),
|
||||||
|
BindTokenBad,
|
||||||
|
BindTokenOk(Uuid),
|
||||||
|
|
||||||
|
// Sign up
|
||||||
|
SignUpRequest(EmailString, UsernameString),
|
||||||
|
SignUpSuccess,
|
||||||
|
SignUpPairTaken,
|
||||||
|
|
||||||
|
// invitations
|
||||||
|
InvitationListLoad,
|
||||||
|
InvitationListLoaded(Vec<Invitation>),
|
||||||
|
//
|
||||||
|
InvitedUsersLoad,
|
||||||
|
InvitedUsersLoaded(Vec<User>),
|
||||||
|
//
|
||||||
|
InvitationSendRequest {
|
||||||
|
name: UsernameString,
|
||||||
|
email: EmailString,
|
||||||
|
role: UserRole,
|
||||||
|
},
|
||||||
|
InvitationSendSuccess,
|
||||||
|
InvitationSendFailure,
|
||||||
|
//
|
||||||
|
InvitationRevokeRequest(InvitationId),
|
||||||
|
InvitationRevokeSuccess(InvitationId),
|
||||||
|
//
|
||||||
|
InvitationAcceptRequest(InvitationToken),
|
||||||
|
InvitationAcceptSuccess(BindToken),
|
||||||
|
InvitationAcceptFailure(InvitationToken),
|
||||||
|
//
|
||||||
|
InvitationRejectRequest(InvitationToken),
|
||||||
|
InvitationRejectSuccess,
|
||||||
|
InvitationRejectFailure(InvitationToken),
|
||||||
|
//
|
||||||
|
InvitedUserRemoveRequest(UserId),
|
||||||
|
InvitedUserRemoveSuccess(UserId),
|
||||||
|
|
||||||
|
// project page
|
||||||
|
ProjectsLoad,
|
||||||
|
ProjectsLoaded(Vec<Project>),
|
||||||
|
|
||||||
|
ProjectIssuesLoad,
|
||||||
|
ProjectIssuesLoaded(Vec<Issue>),
|
||||||
|
ProjectUsersLoad,
|
||||||
|
ProjectUsersLoaded(Vec<User>),
|
||||||
|
ProjectUpdateLoad(UpdateProjectPayload),
|
||||||
|
|
||||||
|
// issue
|
||||||
|
IssueUpdate(IssueId, IssueFieldId, PayloadVariant),
|
||||||
|
IssueUpdated(Issue),
|
||||||
|
IssueDelete(IssueId),
|
||||||
|
IssueDeleted(IssueId),
|
||||||
|
IssueCreate(CreateIssuePayload),
|
||||||
|
IssueCreated(Issue),
|
||||||
|
|
||||||
|
// issue status
|
||||||
|
IssueStatusesLoad,
|
||||||
|
IssueStatusesLoaded(Vec<IssueStatus>),
|
||||||
|
IssueStatusUpdate(IssueStatusId, TitleString, Position),
|
||||||
|
IssueStatusUpdated(IssueStatus),
|
||||||
|
IssueStatusCreate(TitleString, Position),
|
||||||
|
IssueStatusCreated(IssueStatus),
|
||||||
|
IssueStatusDelete(IssueStatusId),
|
||||||
|
IssueStatusDeleted(IssueStatusId),
|
||||||
|
|
||||||
|
// comments
|
||||||
|
IssueCommentsLoad(IssueId),
|
||||||
|
IssueCommentsLoaded(Vec<Comment>),
|
||||||
|
CommentCreate(CreateCommentPayload),
|
||||||
|
CommentCreated(Comment),
|
||||||
|
CommentUpdate(UpdateCommentPayload),
|
||||||
|
CommentUpdated(Comment),
|
||||||
|
CommentDelete(CommentId),
|
||||||
|
CommentDeleted(CommentId),
|
||||||
|
|
||||||
|
// users
|
||||||
|
AvatarUrlChanged(UserId, String),
|
||||||
|
ProfileUpdate(EmailString, UsernameString),
|
||||||
|
ProfileUpdated,
|
||||||
|
|
||||||
|
// user projects
|
||||||
|
UserProjectsLoad,
|
||||||
|
UserProjectsLoaded(Vec<UserProject>),
|
||||||
|
UserProjectSetCurrent(UserProjectId),
|
||||||
|
UserProjectCurrentChanged(UserProject),
|
||||||
|
|
||||||
|
// messages
|
||||||
|
Message(Message),
|
||||||
|
MessagesLoad,
|
||||||
|
MessagesLoaded(Vec<Message>),
|
||||||
|
MessageMarkSeen(MessageId),
|
||||||
|
MessageMarkedSeen(MessageId),
|
||||||
|
|
||||||
|
// epics
|
||||||
|
EpicsLoad,
|
||||||
|
EpicsLoaded(Vec<Epic>),
|
||||||
|
EpicCreate(NameString),
|
||||||
|
EpicCreated(Epic),
|
||||||
|
EpicUpdate(EpicId, NameString),
|
||||||
|
EpicUpdated(Epic),
|
||||||
|
EpicDelete(EpicId),
|
||||||
|
EpicDeleted(EpicId),
|
||||||
|
}
|
94
jirs-data/src/payloads.rs
Normal file
94
jirs-data/src/payloads.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
CommentId, EpicId, Issue, IssueId, IssuePriority, IssueStatusId, IssueType, ProjectCategory,
|
||||||
|
ProjectId, TimeTracking, UserId,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct CreateCommentPayload {
|
||||||
|
pub user_id: Option<UserId>,
|
||||||
|
pub issue_id: IssueId,
|
||||||
|
pub body: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct UpdateCommentPayload {
|
||||||
|
pub id: CommentId,
|
||||||
|
pub body: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||||
|
pub struct CreateIssuePayload {
|
||||||
|
pub title: String,
|
||||||
|
pub issue_type: IssueType,
|
||||||
|
pub priority: IssuePriority,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub description_text: Option<String>,
|
||||||
|
pub estimate: Option<i32>,
|
||||||
|
pub time_spent: Option<i32>,
|
||||||
|
pub time_remaining: Option<i32>,
|
||||||
|
pub project_id: ProjectId,
|
||||||
|
pub user_ids: Vec<UserId>,
|
||||||
|
pub reporter_id: UserId,
|
||||||
|
pub issue_status_id: IssueStatusId,
|
||||||
|
pub epic_id: Option<EpicId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||||
|
pub struct UpdateProjectPayload {
|
||||||
|
pub id: ProjectId,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub url: Option<String>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub category: Option<ProjectCategory>,
|
||||||
|
pub time_tracking: Option<TimeTracking>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
|
pub enum PayloadVariant {
|
||||||
|
OptionI32(Option<i32>),
|
||||||
|
VecI32(Vec<i32>),
|
||||||
|
I32(i32),
|
||||||
|
String(String),
|
||||||
|
IssueType(IssueType),
|
||||||
|
IssuePriority(IssuePriority),
|
||||||
|
ProjectCategory(ProjectCategory),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, PartialOrd)]
|
||||||
|
pub struct UpdateIssuePayload {
|
||||||
|
pub title: String,
|
||||||
|
pub issue_type: IssueType,
|
||||||
|
pub priority: IssuePriority,
|
||||||
|
pub list_position: i32,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub description_text: Option<String>,
|
||||||
|
pub estimate: Option<i32>,
|
||||||
|
pub time_spent: Option<i32>,
|
||||||
|
pub time_remaining: Option<i32>,
|
||||||
|
pub project_id: ProjectId,
|
||||||
|
pub reporter_id: UserId,
|
||||||
|
pub issue_status_id: IssueStatusId,
|
||||||
|
pub user_ids: Vec<UserId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Issue> for UpdateIssuePayload {
|
||||||
|
fn from(issue: Issue) -> Self {
|
||||||
|
Self {
|
||||||
|
title: issue.title,
|
||||||
|
issue_type: issue.issue_type,
|
||||||
|
priority: issue.priority,
|
||||||
|
list_position: issue.list_position,
|
||||||
|
description: issue.description,
|
||||||
|
description_text: issue.description_text,
|
||||||
|
estimate: issue.estimate,
|
||||||
|
time_spent: issue.time_spent,
|
||||||
|
time_remaining: issue.time_remaining,
|
||||||
|
project_id: issue.project_id,
|
||||||
|
reporter_id: issue.reporter_id,
|
||||||
|
user_ids: issue.user_ids,
|
||||||
|
issue_status_id: issue.issue_status_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
136
jirs-server/src/db/epics.rs
Normal file
136
jirs-server/src/db/epics.rs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
use actix::{Handler, Message};
|
||||||
|
use diesel::pg::Pg;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use jirs_data::Epic;
|
||||||
|
|
||||||
|
use crate::db::DbExecutor;
|
||||||
|
use crate::errors::ServiceErrors;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct LoadEpics {
|
||||||
|
pub project_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for LoadEpics {
|
||||||
|
type Result = Result<Vec<Epic>, ServiceErrors>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<LoadEpics> for DbExecutor {
|
||||||
|
type Result = Result<Vec<Epic>, ServiceErrors>;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: LoadEpics, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
use crate::schema::epics::dsl::*;
|
||||||
|
|
||||||
|
let conn = &self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||||
|
|
||||||
|
let epics_query = epics.distinct_on(id).filter(project_id.eq(msg.project_id));
|
||||||
|
debug!("{}", diesel::debug_query::<Pg, _>(&epics_query));
|
||||||
|
epics_query
|
||||||
|
.load(conn)
|
||||||
|
.map_err(|_| ServiceErrors::RecordNotFound("epics".to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct CreateEpic {
|
||||||
|
pub user_id: i32,
|
||||||
|
pub project_id: i32,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for CreateEpic {
|
||||||
|
type Result = Result<Epic, ServiceErrors>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<CreateEpic> for DbExecutor {
|
||||||
|
type Result = Result<Epic, ServiceErrors>;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: CreateEpic, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
use crate::schema::epics::dsl::*;
|
||||||
|
|
||||||
|
let conn = &self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||||
|
|
||||||
|
let epic_query = diesel::insert_into(epics).values((
|
||||||
|
name.eq(msg.name.as_str()),
|
||||||
|
user_id.eq(msg.user_id),
|
||||||
|
project_id.eq(msg.project_id),
|
||||||
|
));
|
||||||
|
debug!("{}", diesel::debug_query::<Pg, _>(&epic_query));
|
||||||
|
epic_query
|
||||||
|
.get_result::<Epic>(conn)
|
||||||
|
.map_err(|_| ServiceErrors::RecordNotFound("epics".to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct UpdateEpic {
|
||||||
|
pub epic_id: i32,
|
||||||
|
pub project_id: i32,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for UpdateEpic {
|
||||||
|
type Result = Result<Epic, ServiceErrors>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<UpdateEpic> for DbExecutor {
|
||||||
|
type Result = Result<Epic, ServiceErrors>;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: UpdateEpic, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
use crate::schema::epics::dsl::*;
|
||||||
|
|
||||||
|
let conn = &self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||||
|
let query = diesel::update(
|
||||||
|
epics
|
||||||
|
.filter(project_id.eq(msg.project_id))
|
||||||
|
.find(msg.epic_id),
|
||||||
|
)
|
||||||
|
.set(name.eq(msg.name));
|
||||||
|
info!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||||
|
let row: Epic = query
|
||||||
|
.get_result::<Epic>(conn)
|
||||||
|
.map_err(|_| ServiceErrors::RecordNotFound("epics".to_string()))?;
|
||||||
|
Ok(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct DeleteEpic {
|
||||||
|
pub epic_id: i32,
|
||||||
|
pub user_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for DeleteEpic {
|
||||||
|
type Result = Result<(), ServiceErrors>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler<DeleteEpic> for DbExecutor {
|
||||||
|
type Result = Result<(), ServiceErrors>;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: DeleteEpic, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
use crate::schema::epics::dsl::*;
|
||||||
|
|
||||||
|
let conn = &self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||||
|
|
||||||
|
let comment_query = diesel::delete(epics.filter(user_id.eq(msg.user_id)).find(msg.epic_id));
|
||||||
|
debug!("{}", diesel::debug_query::<Pg, _>(&comment_query));
|
||||||
|
comment_query
|
||||||
|
.execute(conn)
|
||||||
|
.map_err(|_| ServiceErrors::RecordNotFound("epics".to_string()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
pub mod authorize_user;
|
pub mod authorize_user;
|
||||||
pub mod comments;
|
pub mod comments;
|
||||||
|
pub mod epics;
|
||||||
pub mod invitations;
|
pub mod invitations;
|
||||||
pub mod issue_assignees;
|
pub mod issue_assignees;
|
||||||
pub mod issue_statuses;
|
pub mod issue_statuses;
|
||||||
|
@ -59,10 +59,9 @@ impl WsHandler<CreateCommentPayload> for WebSocketActor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WsHandler<UpdateCommentPayload> for WebSocketActor {
|
impl WsHandler<UpdateCommentPayload> for WebSocketActor {
|
||||||
fn handle_msg(&mut self, msg: UpdateCommentPayload, ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, msg: UpdateCommentPayload, _ctx: &mut Self::Context) -> WsResult {
|
||||||
use crate::db::comments::UpdateComment;
|
use crate::db::comments::UpdateComment;
|
||||||
|
|
||||||
info!("{:?}", msg);
|
|
||||||
let user_id = self.require_user()?.id;
|
let user_id = self.require_user()?.id;
|
||||||
|
|
||||||
let UpdateCommentPayload {
|
let UpdateCommentPayload {
|
||||||
@ -70,12 +69,12 @@ impl WsHandler<UpdateCommentPayload> for WebSocketActor {
|
|||||||
body,
|
body,
|
||||||
} = msg;
|
} = msg;
|
||||||
|
|
||||||
let issue_id = match block_on(self.db.send(UpdateComment {
|
let comment = match block_on(self.db.send(UpdateComment {
|
||||||
comment_id,
|
comment_id,
|
||||||
user_id,
|
user_id,
|
||||||
body,
|
body,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(comment)) => comment.issue_id,
|
Ok(Ok(comment)) => comment,
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
error!("{:?}", e);
|
error!("{:?}", e);
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -85,9 +84,7 @@ impl WsHandler<UpdateCommentPayload> for WebSocketActor {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(v) = self.handle_msg(LoadIssueComments { issue_id }, ctx)? {
|
self.broadcast(&WsMsg::CommentUpdated(comment));
|
||||||
self.broadcast(&v);
|
|
||||||
}
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
79
jirs-server/src/ws/epics.rs
Normal file
79
jirs-server/src/ws/epics.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use futures::executor::block_on;
|
||||||
|
|
||||||
|
use jirs_data::{EpicId, NameString, UserProject, WsMsg};
|
||||||
|
|
||||||
|
use crate::ws::{WebSocketActor, WsHandler, WsResult};
|
||||||
|
|
||||||
|
pub struct LoadEpics;
|
||||||
|
|
||||||
|
impl WsHandler<LoadEpics> for WebSocketActor {
|
||||||
|
fn handle_msg(&mut self, _msg: LoadEpics, _ctx: &mut Self::Context) -> WsResult {
|
||||||
|
let project_id = self.require_user_project()?.project_id;
|
||||||
|
let epics = query_db_or_print!(self, crate::db::epics::LoadEpics { project_id });
|
||||||
|
Ok(Some(WsMsg::EpicsLoaded(epics)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CreateEpic {
|
||||||
|
pub name: NameString,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WsHandler<CreateEpic> for WebSocketActor {
|
||||||
|
fn handle_msg(&mut self, msg: CreateEpic, _ctx: &mut Self::Context) -> WsResult {
|
||||||
|
let CreateEpic { name } = msg;
|
||||||
|
let UserProject {
|
||||||
|
user_id,
|
||||||
|
project_id,
|
||||||
|
..
|
||||||
|
} = self.require_user_project()?;
|
||||||
|
let epic = query_db_or_print!(
|
||||||
|
self,
|
||||||
|
crate::db::epics::CreateEpic {
|
||||||
|
user_id: *user_id,
|
||||||
|
project_id: *project_id,
|
||||||
|
name,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Ok(Some(WsMsg::EpicCreated(epic)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateEpic {
|
||||||
|
pub epic_id: EpicId,
|
||||||
|
pub name: NameString,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WsHandler<UpdateEpic> for WebSocketActor {
|
||||||
|
fn handle_msg(&mut self, msg: UpdateEpic, _ctx: &mut Self::Context) -> WsResult {
|
||||||
|
let UpdateEpic { epic_id, name } = msg;
|
||||||
|
let UserProject { project_id, .. } = self.require_user_project()?;
|
||||||
|
let epic = query_db_or_print!(
|
||||||
|
self,
|
||||||
|
crate::db::epics::UpdateEpic {
|
||||||
|
project_id: *project_id,
|
||||||
|
epic_id: epic_id,
|
||||||
|
name: name.clone(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Ok(Some(WsMsg::EpicUpdated(epic)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DeleteEpic {
|
||||||
|
pub epic_id: EpicId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WsHandler<DeleteEpic> for WebSocketActor {
|
||||||
|
fn handle_msg(&mut self, msg: DeleteEpic, _ctx: &mut Self::Context) -> WsResult {
|
||||||
|
let DeleteEpic { epic_id } = msg;
|
||||||
|
let UserProject { user_id, .. } = self.require_user_project()?;
|
||||||
|
query_db_or_print!(
|
||||||
|
self,
|
||||||
|
crate::db::epics::DeleteEpic {
|
||||||
|
user_id: *user_id,
|
||||||
|
epic_id: epic_id,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Ok(Some(WsMsg::EpicDeleted(epic_id)))
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@ impl WsHandler<LoadIssueStatuses> for WebSocketActor {
|
|||||||
self.db
|
self.db
|
||||||
.send(issue_statuses::LoadIssueStatuses { project_id }),
|
.send(issue_statuses::LoadIssueStatuses { project_id }),
|
||||||
) {
|
) {
|
||||||
Ok(Ok(v)) => Some(WsMsg::IssueStatusesResponse(v)),
|
Ok(Ok(v)) => Some(WsMsg::IssueStatusesLoaded(v)),
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
error!("{:?}", e);
|
error!("{:?}", e);
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
@ -11,7 +11,7 @@ impl WsHandler<LoadMessages> for WebSocketActor {
|
|||||||
fn handle_msg(&mut self, _msg: LoadMessages, _ctx: &mut Self::Context) -> WsResult {
|
fn handle_msg(&mut self, _msg: LoadMessages, _ctx: &mut Self::Context) -> WsResult {
|
||||||
let user_id = self.require_user()?.id;
|
let user_id = self.require_user()?.id;
|
||||||
match block_on(self.db.send(messages::LoadMessages { user_id })) {
|
match block_on(self.db.send(messages::LoadMessages { user_id })) {
|
||||||
Ok(Ok(v)) => Ok(Some(WsMsg::MessagesResponse(v))),
|
Ok(Ok(v)) => Ok(Some(WsMsg::MessagesLoaded(v))),
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
error!("{:?}", e);
|
error!("{:?}", e);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -3,29 +3,49 @@ use std::collections::HashMap;
|
|||||||
use actix::{
|
use actix::{
|
||||||
Actor, ActorContext, Addr, AsyncContext, Context, Handler, Message, Recipient, StreamHandler,
|
Actor, ActorContext, Addr, AsyncContext, Context, Handler, Message, Recipient, StreamHandler,
|
||||||
};
|
};
|
||||||
use actix_web::web::Data;
|
use actix_web::{
|
||||||
use actix_web::{get, web, Error, HttpRequest, HttpResponse};
|
get,
|
||||||
|
web::{self, Data},
|
||||||
|
Error, HttpRequest, HttpResponse,
|
||||||
|
};
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
|
|
||||||
use jirs_data::{Project, ProjectId, User, UserId, UserProject, WsMsg};
|
use jirs_data::{Project, ProjectId, User, UserId, UserProject, WsMsg};
|
||||||
|
|
||||||
use crate::db::projects::LoadCurrentProject;
|
use crate::db::{projects::LoadCurrentProject, user_projects::CurrentUserProject, DbExecutor};
|
||||||
use crate::db::user_projects::CurrentUserProject;
|
|
||||||
use crate::db::DbExecutor;
|
|
||||||
use crate::mail::MailExecutor;
|
use crate::mail::MailExecutor;
|
||||||
use crate::ws::auth::*;
|
use crate::ws::{
|
||||||
use crate::ws::comments::*;
|
auth::*,
|
||||||
use crate::ws::invitations::*;
|
comments::*,
|
||||||
use crate::ws::issue_statuses::*;
|
invitations::*,
|
||||||
use crate::ws::issues::*;
|
issue_statuses::*,
|
||||||
use crate::ws::messages::*;
|
issues::*,
|
||||||
use crate::ws::projects::*;
|
messages::*,
|
||||||
use crate::ws::user_projects::{LoadUserProjects, SetCurrentUserProject};
|
projects::*,
|
||||||
use crate::ws::users::*;
|
user_projects::{LoadUserProjects, SetCurrentUserProject},
|
||||||
|
users::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! query_db_or_print {
|
||||||
|
($s:expr,$msg:expr) => {
|
||||||
|
match block_on($s.db.send($msg)) {
|
||||||
|
Ok(Ok(r)) => r,
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod comments;
|
pub mod comments;
|
||||||
|
pub mod epics;
|
||||||
pub mod invitations;
|
pub mod invitations;
|
||||||
pub mod issue_statuses;
|
pub mod issue_statuses;
|
||||||
pub mod issues;
|
pub mod issues;
|
||||||
@ -93,7 +113,7 @@ impl WebSocketActor {
|
|||||||
WsMsg::Pong => Some(WsMsg::Ping),
|
WsMsg::Pong => Some(WsMsg::Ping),
|
||||||
|
|
||||||
// issues
|
// issues
|
||||||
WsMsg::IssueUpdateRequest(id, field_id, payload) => self.handle_msg(
|
WsMsg::IssueUpdate(id, field_id, payload) => self.handle_msg(
|
||||||
UpdateIssueHandler {
|
UpdateIssueHandler {
|
||||||
id,
|
id,
|
||||||
field_id,
|
field_id,
|
||||||
@ -101,12 +121,12 @@ impl WebSocketActor {
|
|||||||
},
|
},
|
||||||
ctx,
|
ctx,
|
||||||
)?,
|
)?,
|
||||||
WsMsg::IssueCreateRequest(payload) => self.handle_msg(payload, ctx)?,
|
WsMsg::IssueCreate(payload) => self.handle_msg(payload, ctx)?,
|
||||||
WsMsg::IssueDeleteRequest(id) => self.handle_msg(DeleteIssue { id }, ctx)?,
|
WsMsg::IssueDelete(id) => self.handle_msg(DeleteIssue { id }, ctx)?,
|
||||||
WsMsg::ProjectIssuesRequest => self.handle_msg(LoadIssues, ctx)?,
|
WsMsg::ProjectIssuesLoad => self.handle_msg(LoadIssues, ctx)?,
|
||||||
|
|
||||||
// issue statuses
|
// issue statuses
|
||||||
WsMsg::IssueStatusesRequest => self.handle_msg(LoadIssueStatuses, ctx)?,
|
WsMsg::IssueStatusesLoad => self.handle_msg(LoadIssueStatuses, ctx)?,
|
||||||
WsMsg::IssueStatusDelete(issue_status_id) => {
|
WsMsg::IssueStatusDelete(issue_status_id) => {
|
||||||
self.handle_msg(DeleteIssueStatus { issue_status_id }, ctx)?
|
self.handle_msg(DeleteIssueStatus { issue_status_id }, ctx)?
|
||||||
}
|
}
|
||||||
@ -124,7 +144,7 @@ impl WebSocketActor {
|
|||||||
|
|
||||||
// projects
|
// projects
|
||||||
WsMsg::ProjectsLoad => self.handle_msg(LoadProjects, ctx)?,
|
WsMsg::ProjectsLoad => self.handle_msg(LoadProjects, ctx)?,
|
||||||
WsMsg::ProjectUpdateRequest(payload) => self.handle_msg(payload, ctx)?,
|
WsMsg::ProjectUpdateLoad(payload) => self.handle_msg(payload, ctx)?,
|
||||||
|
|
||||||
// user projects
|
// user projects
|
||||||
WsMsg::UserProjectsLoad => self.handle_msg(LoadUserProjects, ctx)?,
|
WsMsg::UserProjectsLoad => self.handle_msg(LoadUserProjects, ctx)?,
|
||||||
@ -136,9 +156,7 @@ impl WebSocketActor {
|
|||||||
)?,
|
)?,
|
||||||
|
|
||||||
// auth
|
// auth
|
||||||
WsMsg::AuthorizeRequest(uuid) => {
|
WsMsg::AuthorizeLoad(uuid) => self.handle_msg(CheckAuthToken { token: uuid }, ctx)?,
|
||||||
self.handle_msg(CheckAuthToken { token: uuid }, ctx)?
|
|
||||||
}
|
|
||||||
WsMsg::BindTokenCheck(uuid) => {
|
WsMsg::BindTokenCheck(uuid) => {
|
||||||
self.handle_msg(CheckBindToken { bind_token: uuid }, ctx)?
|
self.handle_msg(CheckBindToken { bind_token: uuid }, ctx)?
|
||||||
}
|
}
|
||||||
@ -156,18 +174,18 @@ impl WebSocketActor {
|
|||||||
)?,
|
)?,
|
||||||
|
|
||||||
// users
|
// users
|
||||||
WsMsg::ProjectUsersRequest => self.handle_msg(LoadProjectUsers, ctx)?,
|
WsMsg::ProjectUsersLoad => self.handle_msg(LoadProjectUsers, ctx)?,
|
||||||
WsMsg::InvitedUserRemoveRequest(user_id) => {
|
WsMsg::InvitedUserRemoveRequest(user_id) => {
|
||||||
self.handle_msg(RemoveInvitedUser { user_id }, ctx)?
|
self.handle_msg(RemoveInvitedUser { user_id }, ctx)?
|
||||||
}
|
}
|
||||||
|
|
||||||
// comments
|
// comments
|
||||||
WsMsg::IssueCommentsRequest(issue_id) => {
|
WsMsg::IssueCommentsLoad(issue_id) => {
|
||||||
self.handle_msg(LoadIssueComments { issue_id }, ctx)?
|
self.handle_msg(LoadIssueComments { issue_id }, ctx)?
|
||||||
}
|
}
|
||||||
WsMsg::CreateComment(payload) => self.handle_msg(payload, ctx)?,
|
WsMsg::CommentCreate(payload) => self.handle_msg(payload, ctx)?,
|
||||||
WsMsg::UpdateComment(payload) => self.handle_msg(payload, ctx)?,
|
WsMsg::CommentUpdate(payload) => self.handle_msg(payload, ctx)?,
|
||||||
WsMsg::CommentDeleteRequest(comment_id) => {
|
WsMsg::CommentDelete(comment_id) => {
|
||||||
self.handle_msg(DeleteComment { comment_id }, ctx)?
|
self.handle_msg(DeleteComment { comment_id }, ctx)?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,12 +193,12 @@ impl WebSocketActor {
|
|||||||
WsMsg::InvitationSendRequest { name, email, role } => {
|
WsMsg::InvitationSendRequest { name, email, role } => {
|
||||||
self.handle_msg(CreateInvitation { name, email, role }, ctx)?
|
self.handle_msg(CreateInvitation { name, email, role }, ctx)?
|
||||||
}
|
}
|
||||||
WsMsg::InvitationListRequest => self.handle_msg(ListInvitation, ctx)?,
|
WsMsg::InvitationListLoad => self.handle_msg(ListInvitation, ctx)?,
|
||||||
WsMsg::InvitationAcceptRequest(invitation_token) => {
|
WsMsg::InvitationAcceptRequest(invitation_token) => {
|
||||||
self.handle_msg(AcceptInvitation { invitation_token }, ctx)?
|
self.handle_msg(AcceptInvitation { invitation_token }, ctx)?
|
||||||
}
|
}
|
||||||
WsMsg::InvitationRevokeRequest(id) => self.handle_msg(RevokeInvitation { id }, ctx)?,
|
WsMsg::InvitationRevokeRequest(id) => self.handle_msg(RevokeInvitation { id }, ctx)?,
|
||||||
WsMsg::InvitedUsersRequest => self.handle_msg(LoadInvitedUsers, ctx)?,
|
WsMsg::InvitedUsersLoad => self.handle_msg(LoadInvitedUsers, ctx)?,
|
||||||
|
|
||||||
// users
|
// users
|
||||||
WsMsg::ProfileUpdate(email, name) => {
|
WsMsg::ProfileUpdate(email, name) => {
|
||||||
@ -188,9 +206,17 @@ impl WebSocketActor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// messages
|
// messages
|
||||||
WsMsg::MessagesRequest => self.handle_msg(LoadMessages, ctx)?,
|
WsMsg::MessagesLoad => self.handle_msg(LoadMessages, ctx)?,
|
||||||
WsMsg::MessageMarkSeen(id) => self.handle_msg(MarkMessageSeen { id }, ctx)?,
|
WsMsg::MessageMarkSeen(id) => self.handle_msg(MarkMessageSeen { id }, ctx)?,
|
||||||
|
|
||||||
|
// epics
|
||||||
|
WsMsg::EpicsLoad => self.handle_msg(epics::LoadEpics, ctx)?,
|
||||||
|
WsMsg::EpicCreate(name) => self.handle_msg(epics::CreateEpic { name }, ctx)?,
|
||||||
|
WsMsg::EpicUpdate(epic_id, name) => {
|
||||||
|
self.handle_msg(epics::UpdateEpic { epic_id, name }, ctx)?
|
||||||
|
}
|
||||||
|
WsMsg::EpicDelete(epic_id) => self.handle_msg(epics::DeleteEpic { epic_id }, ctx)?,
|
||||||
|
|
||||||
// else fail
|
// else fail
|
||||||
_ => {
|
_ => {
|
||||||
error!("No handle for {:?} specified", msg);
|
error!("No handle for {:?} specified", msg);
|
||||||
|
Loading…
Reference in New Issue
Block a user