Fix accept invitation, fix link send to user. Change state of form, display invitations and users
This commit is contained in:
parent
4316f6888d
commit
ff0d4bb329
30
jirs-client/js/css/users.css
Normal file
30
jirs-client/js/css/users.css
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#users > .usersSection,
|
||||||
|
#users > .invitationsSection {
|
||||||
|
padding: 25px 40px 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#users > .usersSection > .usersList,
|
||||||
|
#users > .invitationsSection > .invitationsList {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#users > .usersSection > .usersList > .user,
|
||||||
|
#users > .invitationsSection > .invitationsList > .invitation {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#users > .invitationsSection > .invitationsList > .invitation > * {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#users .invitationActions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#users .invitationActions > .error {
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
@ -27,3 +27,4 @@
|
|||||||
@import "./css/timeTracking.css";
|
@import "./css/timeTracking.css";
|
||||||
@import "./css/login.css";
|
@import "./css/login.css";
|
||||||
@import "./css/register.css";
|
@import "./css/register.css";
|
||||||
|
@import "./css/users.css";
|
||||||
|
@ -11,25 +11,19 @@ use crate::validations::is_token;
|
|||||||
use crate::{FieldId, Msg};
|
use crate::{FieldId, Msg};
|
||||||
|
|
||||||
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>) {
|
||||||
match msg {
|
if let Msg::ChangePage(Page::Project) = msg {
|
||||||
Msg::ChangePage(Page::Project) => {
|
model.page_content = PageContent::Invite(Box::new(InvitePage::default()));
|
||||||
model.page_content = PageContent::Invite(InvitePage::default());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
let page = match &mut model.page_content {
|
let page = match &mut model.page_content {
|
||||||
PageContent::Invite(page) => page,
|
PageContent::Invite(page) => page,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
match msg {
|
if let Msg::InputChanged(FieldId::Invite(InviteFieldId::Token), text) = msg {
|
||||||
Msg::InputChanged(FieldId::Invite(InviteFieldId::Token), text) => {
|
|
||||||
page.token_touched = true;
|
page.token_touched = true;
|
||||||
page.token = text.clone();
|
page.token = text;
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,6 +153,7 @@ pub enum Msg {
|
|||||||
AuthTokenErased,
|
AuthTokenErased,
|
||||||
SignInRequest,
|
SignInRequest,
|
||||||
BindClientRequest,
|
BindClientRequest,
|
||||||
|
InviteRequest,
|
||||||
|
|
||||||
// sign up
|
// sign up
|
||||||
SignUpRequest,
|
SignUpRequest,
|
||||||
@ -360,7 +361,7 @@ fn authorize_or_redirect() {
|
|||||||
Err(..) => {
|
Err(..) => {
|
||||||
let pathname = seed::document().location().unwrap().pathname().unwrap();
|
let pathname = seed::document().location().unwrap().pathname().unwrap();
|
||||||
match pathname.as_str() {
|
match pathname.as_str() {
|
||||||
"/login" | "/register" => {}
|
"/login" | "/register" | "/invite" => {}
|
||||||
_ => {
|
_ => {
|
||||||
seed::push_route(vec!["login"]);
|
seed::push_route(vec!["login"]);
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,9 @@ pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orde
|
|||||||
|
|
||||||
let payload = jirs_data::CreateIssuePayload {
|
let payload = jirs_data::CreateIssuePayload {
|
||||||
title: modal.title.clone(),
|
title: modal.title.clone(),
|
||||||
issue_type: modal.issue_type.clone(),
|
issue_type: modal.issue_type,
|
||||||
status: modal.status.clone(),
|
status: modal.status,
|
||||||
priority: modal.priority.clone(),
|
priority: modal.priority,
|
||||||
description: modal.description.clone(),
|
description: modal.description.clone(),
|
||||||
description_text: modal.description_text.clone(),
|
description_text: modal.description_text.clone(),
|
||||||
estimate: modal.estimate,
|
estimate: modal.estimate,
|
||||||
|
@ -40,7 +40,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Type,
|
IssueFieldId::Type,
|
||||||
PayloadVariant::IssueType(modal.payload.issue_type.clone()),
|
PayloadVariant::IssueType(modal.payload.issue_type),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Msg::StyledSelectChanged(
|
Msg::StyledSelectChanged(
|
||||||
@ -51,7 +51,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::Status,
|
IssueFieldId::Status,
|
||||||
PayloadVariant::IssueStatus(modal.payload.status.clone()),
|
PayloadVariant::IssueStatus(modal.payload.status),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Msg::StyledSelectChanged(
|
Msg::StyledSelectChanged(
|
||||||
@ -143,7 +143,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::TimeSpend,
|
IssueFieldId::TimeSpend,
|
||||||
PayloadVariant::OptionI32(modal.payload.time_spent.clone()),
|
PayloadVariant::OptionI32(modal.payload.time_spent),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Msg::InputChanged(
|
Msg::InputChanged(
|
||||||
@ -154,7 +154,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::TimeRemaining,
|
IssueFieldId::TimeRemaining,
|
||||||
PayloadVariant::OptionI32(modal.payload.time_remaining.clone()),
|
PayloadVariant::OptionI32(modal.payload.time_remaining),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Msg::ModalChanged(FieldChange::TabChanged(
|
Msg::ModalChanged(FieldChange::TabChanged(
|
||||||
@ -189,7 +189,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::TimeRemaining,
|
IssueFieldId::TimeRemaining,
|
||||||
PayloadVariant::OptionI32(modal.payload.estimate.clone()),
|
PayloadVariant::OptionI32(modal.payload.estimate),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
_ if value.is_empty() => {
|
_ if value.is_empty() => {
|
||||||
@ -197,7 +197,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||||
modal.id,
|
modal.id,
|
||||||
IssueFieldId::TimeRemaining,
|
IssueFieldId::TimeRemaining,
|
||||||
PayloadVariant::OptionI32(modal.payload.estimate.clone()),
|
PayloadVariant::OptionI32(modal.payload.estimate),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -50,7 +50,7 @@ pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>
|
|||||||
Msg::ChangePage(Page::AddIssue) => {
|
Msg::ChangePage(Page::AddIssue) => {
|
||||||
let mut modal = AddIssueModal::default();
|
let mut modal = AddIssueModal::default();
|
||||||
modal.project_id = model.project.as_ref().map(|p| p.id);
|
modal.project_id = model.project.as_ref().map(|p| p.id);
|
||||||
model.modals.push(ModalType::AddIssue(modal));
|
model.modals.push(ModalType::AddIssue(Box::new(modal)));
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -101,7 +101,7 @@ fn push_edit_modal(issue_id: i32, model: &mut Model) {
|
|||||||
Some(issue) => issue,
|
Some(issue) => issue,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
ModalType::EditIssue(issue_id, EditIssueModal::new(issue))
|
ModalType::EditIssue(issue_id, Box::new(EditIssueModal::new(issue)))
|
||||||
};
|
};
|
||||||
send_ws_msg(WsMsg::IssueCommentsRequest(issue_id));
|
send_ws_msg(WsMsg::IssueCommentsRequest(issue_id));
|
||||||
model.modals.push(modal);
|
model.modals.push(modal);
|
||||||
|
@ -11,8 +11,8 @@ use crate::{EditIssueModalSection, FieldId, ProjectFieldId, HOST_URL};
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
pub enum ModalType {
|
pub enum ModalType {
|
||||||
AddIssue(AddIssueModal),
|
AddIssue(Box<AddIssueModal>),
|
||||||
EditIssue(IssueId, EditIssueModal),
|
EditIssue(IssueId, Box<EditIssueModal>),
|
||||||
DeleteIssueConfirm(IssueId),
|
DeleteIssueConfirm(IssueId),
|
||||||
DeleteCommentConfirm(CommentId),
|
DeleteCommentConfirm(CommentId),
|
||||||
TimeTracking(IssueId),
|
TimeTracking(IssueId),
|
||||||
@ -49,9 +49,9 @@ impl EditIssueModal {
|
|||||||
link_copied: false,
|
link_copied: false,
|
||||||
payload: UpdateIssuePayload {
|
payload: UpdateIssuePayload {
|
||||||
title: issue.title.clone(),
|
title: issue.title.clone(),
|
||||||
issue_type: issue.issue_type.clone(),
|
issue_type: issue.issue_type,
|
||||||
status: issue.status.clone(),
|
status: issue.status,
|
||||||
priority: issue.priority.clone(),
|
priority: issue.priority,
|
||||||
list_position: issue.list_position,
|
list_position: issue.list_position,
|
||||||
description: issue.description.clone(),
|
description: issue.description.clone(),
|
||||||
description_text: issue.description_text.clone(),
|
description_text: issue.description_text.clone(),
|
||||||
@ -252,6 +252,20 @@ pub struct SignUpPage {
|
|||||||
pub email_touched: bool,
|
pub email_touched: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialOrd, PartialEq)]
|
||||||
|
pub enum InvitationFormState {
|
||||||
|
Initial = 1,
|
||||||
|
Sent = 2,
|
||||||
|
Succeed = 3,
|
||||||
|
Failed = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InvitationFormState {
|
||||||
|
fn default() -> Self {
|
||||||
|
InvitationFormState::Initial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UsersPage {
|
pub struct UsersPage {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -263,6 +277,10 @@ pub struct UsersPage {
|
|||||||
pub user_role_state: StyledSelectState,
|
pub user_role_state: StyledSelectState,
|
||||||
pub pending_invitations: Vec<String>,
|
pub pending_invitations: Vec<String>,
|
||||||
pub error: String,
|
pub error: String,
|
||||||
|
pub form_state: InvitationFormState,
|
||||||
|
|
||||||
|
pub invited_users: Vec<User>,
|
||||||
|
pub invitations: Vec<Invitation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UsersPage {
|
impl Default for UsersPage {
|
||||||
@ -276,18 +294,21 @@ impl Default for UsersPage {
|
|||||||
user_role_state: StyledSelectState::new(FieldId::Users(UsersFieldId::UserRole)),
|
user_role_state: StyledSelectState::new(FieldId::Users(UsersFieldId::UserRole)),
|
||||||
pending_invitations: vec![],
|
pending_invitations: vec![],
|
||||||
error: "".to_string(),
|
error: "".to_string(),
|
||||||
|
form_state: Default::default(),
|
||||||
|
invited_users: vec![],
|
||||||
|
invitations: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PageContent {
|
pub enum PageContent {
|
||||||
SignIn(SignInPage),
|
SignIn(Box<SignInPage>),
|
||||||
SignUp(SignUpPage),
|
SignUp(Box<SignUpPage>),
|
||||||
Project(ProjectPage),
|
Project(Box<ProjectPage>),
|
||||||
ProjectSettings(ProjectSettingsPage),
|
ProjectSettings(Box<ProjectSettingsPage>),
|
||||||
Invite(InvitePage),
|
Invite(Box<InvitePage>),
|
||||||
Users(UsersPage),
|
Users(Box<UsersPage>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -332,7 +353,7 @@ impl Default for Model {
|
|||||||
comments_by_project_id: Default::default(),
|
comments_by_project_id: Default::default(),
|
||||||
page: Page::Project,
|
page: Page::Project,
|
||||||
host_url,
|
host_url,
|
||||||
page_content: PageContent::Project(ProjectPage::default()),
|
page_content: PageContent::Project(Box::new(ProjectPage::default())),
|
||||||
modals: vec![],
|
modals: vec![],
|
||||||
project: None,
|
project: None,
|
||||||
comments: vec![],
|
comments: vec![],
|
||||||
|
@ -22,7 +22,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(..)) => {
|
||||||
model.page_content = PageContent::Project(ProjectPage::default());
|
model.page_content = PageContent::Project(Box::new(ProjectPage::default()));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -283,7 +283,7 @@ fn project_issue_list(model: &Model, status: jirs_data::IssueStatus) -> Node<Msg
|
|||||||
.issues
|
.issues
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|issue| {
|
.filter(|issue| {
|
||||||
issue_filter_status(issue, &status)
|
issue_filter_status(issue, status)
|
||||||
&& issue_filter_with_text(issue, project_page.text_filter.as_str())
|
&& issue_filter_with_text(issue, project_page.text_filter.as_str())
|
||||||
&& issue_filter_with_only_my(issue, project_page.only_my_filter, &model.user)
|
&& issue_filter_with_only_my(issue, project_page.only_my_filter, &model.user)
|
||||||
&& issue_filter_with_only_recent(issue, ids.as_slice())
|
&& issue_filter_with_only_recent(issue, ids.as_slice())
|
||||||
@ -292,13 +292,13 @@ fn project_issue_list(model: &Model, status: jirs_data::IssueStatus) -> Node<Msg
|
|||||||
.collect();
|
.collect();
|
||||||
let label = status.to_label();
|
let label = status.to_label();
|
||||||
|
|
||||||
let send_status = status.clone();
|
let send_status = status;
|
||||||
let drop_handler = drag_ev(Ev::Drop, move |ev| {
|
let drop_handler = drag_ev(Ev::Drop, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Msg::IssueDropZone(send_status)
|
Msg::IssueDropZone(send_status)
|
||||||
});
|
});
|
||||||
|
|
||||||
let send_status = status.clone();
|
let send_status = status;
|
||||||
let drag_over_handler = drag_ev(Ev::DragOver, move |ev| {
|
let drag_over_handler = drag_ev(Ev::DragOver, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Msg::IssueDragOverStatus(send_status)
|
Msg::IssueDragOverStatus(send_status)
|
||||||
@ -321,8 +321,8 @@ fn project_issue_list(model: &Model, status: jirs_data::IssueStatus) -> Node<Msg
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn issue_filter_status(issue: &Issue, status: &IssueStatus) -> bool {
|
fn issue_filter_status(issue: &Issue, status: IssueStatus) -> bool {
|
||||||
&issue.status == status
|
issue.status == status
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -384,7 +384,7 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
|||||||
ev.stop_propagation();
|
ev.stop_propagation();
|
||||||
Msg::ExchangePosition(issue_id)
|
Msg::ExchangePosition(issue_id)
|
||||||
});
|
});
|
||||||
let issue_id = issue.id.clone();
|
let issue_id = issue.id;
|
||||||
let drag_out = drag_ev(Ev::DragLeave, move |_| Msg::DragLeave(issue_id));
|
let drag_out = drag_ev(Ev::DragLeave, move |_| Msg::DragLeave(issue_id));
|
||||||
|
|
||||||
let class_list = vec!["issue"];
|
let class_list = vec!["issue"];
|
||||||
|
@ -76,7 +76,7 @@ fn build_page_content(model: &mut Model) {
|
|||||||
Some(project) => project,
|
Some(project) => project,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
model.page_content = PageContent::ProjectSettings(ProjectSettingsPage::new(project));
|
model.page_content = PageContent::ProjectSettings(Box::new(ProjectSettingsPage::new(project)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(model: &model::Model) -> Node<Msg> {
|
pub fn view(model: &model::Model) -> Node<Msg> {
|
||||||
|
@ -35,6 +35,7 @@ pub struct StyledButtonBuilder {
|
|||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
children: Option<Vec<Node<Msg>>>,
|
children: Option<Vec<Node<Msg>>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<String>,
|
||||||
|
button_type: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledButtonBuilder {
|
impl StyledButtonBuilder {
|
||||||
@ -107,6 +108,11 @@ impl StyledButtonBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_type_reset(mut self) -> Self {
|
||||||
|
self.button_type = Some("reset".to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledButton {
|
pub fn build(self) -> StyledButton {
|
||||||
StyledButton {
|
StyledButton {
|
||||||
variant: self.variant.unwrap_or_else(|| Variant::Primary),
|
variant: self.variant.unwrap_or_else(|| Variant::Primary),
|
||||||
@ -117,6 +123,7 @@ impl StyledButtonBuilder {
|
|||||||
on_click: self.on_click,
|
on_click: self.on_click,
|
||||||
children: self.children.unwrap_or_default(),
|
children: self.children.unwrap_or_default(),
|
||||||
class_list: self.class_list,
|
class_list: self.class_list,
|
||||||
|
button_type: self.button_type.unwrap_or_else(|| "submit".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,6 +137,7 @@ pub struct StyledButton {
|
|||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
children: Vec<Node<Msg>>,
|
children: Vec<Node<Msg>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<String>,
|
||||||
|
button_type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledButton {
|
impl StyledButton {
|
||||||
@ -154,6 +162,7 @@ pub fn render(values: StyledButton) -> Node<Msg> {
|
|||||||
on_click,
|
on_click,
|
||||||
children,
|
children,
|
||||||
mut class_list,
|
mut class_list,
|
||||||
|
button_type,
|
||||||
} = values;
|
} = values;
|
||||||
class_list.push("styledButton".to_string());
|
class_list.push("styledButton".to_string());
|
||||||
class_list.push(variant.to_string());
|
class_list.push(variant.to_string());
|
||||||
@ -185,6 +194,7 @@ pub fn render(values: StyledButton) -> Node<Msg> {
|
|||||||
seed::button![
|
seed::button![
|
||||||
attrs![
|
attrs![
|
||||||
At::Class => class_list.join(" "),
|
At::Class => class_list.join(" "),
|
||||||
|
At::Type => button_type,
|
||||||
],
|
],
|
||||||
handler,
|
handler,
|
||||||
if disabled {
|
if disabled {
|
||||||
|
@ -183,7 +183,7 @@ pub fn render(values: StyledTextarea) -> Node<Msg> {
|
|||||||
let textarea = seed::to_textarea(&target);
|
let textarea = seed::to_textarea(&target);
|
||||||
let value = textarea.value();
|
let value = textarea.value();
|
||||||
|
|
||||||
if handler_disable_auto_resize && value.contains("\n") {
|
if handler_disable_auto_resize && value.contains('\n') {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if msg == Msg::ChangePage(Page::SignIn) {
|
if msg == Msg::ChangePage(Page::SignIn) {
|
||||||
model.page_content = PageContent::SignIn(SignInPage::default());
|
model.page_content = PageContent::SignIn(Box::new(SignInPage::default()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ pub fn update(msg: Msg, model: &mut model::Model, _orders: &mut impl Orders<Msg>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if msg == Msg::ChangePage(Page::SignUp) {
|
if msg == Msg::ChangePage(Page::SignUp) {
|
||||||
model.page_content = PageContent::SignUp(SignUpPage::default());
|
model.page_content = PageContent::SignUp(Box::new(SignUpPage::default()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
use jirs_data::UserRole;
|
use jirs_data::{ToVec, UserRole, UsersFieldId, WsMsg};
|
||||||
use jirs_data::{ToVec, UsersFieldId};
|
|
||||||
|
|
||||||
use crate::model::{Model, Page, PageContent, UsersPage};
|
use crate::api::send_ws_msg;
|
||||||
|
use crate::model::{InvitationFormState, Model, Page, PageContent, UsersPage};
|
||||||
use crate::shared::styled_button::StyledButton;
|
use crate::shared::styled_button::StyledButton;
|
||||||
use crate::shared::styled_field::StyledField;
|
use crate::shared::styled_field::StyledField;
|
||||||
use crate::shared::styled_form::StyledForm;
|
use crate::shared::styled_form::StyledForm;
|
||||||
@ -15,13 +15,10 @@ use crate::validations::is_email;
|
|||||||
use crate::{FieldId, Msg};
|
use crate::{FieldId, Msg};
|
||||||
|
|
||||||
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>) {
|
||||||
match msg {
|
if let Msg::ChangePage(Page::Users) = msg {
|
||||||
Msg::ChangePage(Page::Users) => {
|
model.page_content = PageContent::Users(Box::new(UsersPage::default()));
|
||||||
model.page_content = PageContent::Users(UsersPage::default());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
let page = match &mut model.page_content {
|
let page = match &mut model.page_content {
|
||||||
PageContent::Users(page) => page,
|
PageContent::Users(page) => page,
|
||||||
@ -31,6 +28,16 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
page.user_role_state.update(&msg, orders);
|
page.user_role_state.update(&msg, orders);
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
|
Msg::WsMsg(WsMsg::AuthorizeLoaded(Ok(_))) | Msg::ChangePage(Page::Users) => {
|
||||||
|
send_ws_msg(WsMsg::InvitationListRequest);
|
||||||
|
send_ws_msg(WsMsg::InvitedUsersRequest);
|
||||||
|
}
|
||||||
|
Msg::WsMsg(WsMsg::InvitedUsersLoaded(users)) => {
|
||||||
|
page.invited_users = users;
|
||||||
|
}
|
||||||
|
Msg::WsMsg(WsMsg::InvitationListLoaded(invitations)) => {
|
||||||
|
page.invitations = invitations;
|
||||||
|
}
|
||||||
Msg::StyledSelectChanged(
|
Msg::StyledSelectChanged(
|
||||||
FieldId::Users(UsersFieldId::UserRole),
|
FieldId::Users(UsersFieldId::UserRole),
|
||||||
StyledSelectChange::Changed(role),
|
StyledSelectChange::Changed(role),
|
||||||
@ -45,6 +52,20 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
page.email = email;
|
page.email = email;
|
||||||
page.email_touched = true;
|
page.email_touched = true;
|
||||||
}
|
}
|
||||||
|
Msg::InviteRequest => {
|
||||||
|
page.form_state = InvitationFormState::Sent;
|
||||||
|
send_ws_msg(WsMsg::InvitationSendRequest {
|
||||||
|
name: page.name.clone(),
|
||||||
|
email: page.email.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Msg::WsMsg(WsMsg::InvitationSendSuccess) => {
|
||||||
|
send_ws_msg(WsMsg::InvitationListRequest);
|
||||||
|
page.form_state = InvitationFormState::Succeed;
|
||||||
|
}
|
||||||
|
Msg::WsMsg(WsMsg::InvitationSendFailure) => {
|
||||||
|
page.form_state = InvitationFormState::Failed;
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,12 +124,27 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
|
|
||||||
let submit = StyledButton::build()
|
let submit = StyledButton::build()
|
||||||
.add_class("submitUserInvite")
|
.add_class("submitUserInvite")
|
||||||
.active(true)
|
.active(page.form_state != InvitationFormState::Sent)
|
||||||
.primary()
|
.primary()
|
||||||
.text("Invite user")
|
.text("Invite user")
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let submit_field = StyledField::build().input(submit).build().into_node();
|
let submit_supplement = match page.form_state {
|
||||||
|
InvitationFormState::Succeed => StyledButton::build()
|
||||||
|
.add_class("resetUserInvite")
|
||||||
|
.active(true)
|
||||||
|
.empty()
|
||||||
|
.set_type_reset()
|
||||||
|
.text("Reset")
|
||||||
|
.build()
|
||||||
|
.into_node(),
|
||||||
|
InvitationFormState::Failed => div![class!["error"], "There was an error"],
|
||||||
|
_ => empty![],
|
||||||
|
};
|
||||||
|
let submit_field = StyledField::build()
|
||||||
|
.input(div![class!["invitationActions"], submit, submit_supplement])
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
|
||||||
let form = StyledForm::build()
|
let form = StyledForm::build()
|
||||||
.heading("Invite new user")
|
.heading("Invite new user")
|
||||||
@ -116,8 +152,59 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
.add_field(email_field)
|
.add_field(email_field)
|
||||||
.add_field(user_role_field)
|
.add_field(user_role_field)
|
||||||
.add_field(submit_field)
|
.add_field(submit_field)
|
||||||
|
.on_submit(ev(Ev::Submit, |ev| {
|
||||||
|
ev.prevent_default();
|
||||||
|
Msg::InviteRequest
|
||||||
|
}))
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
inner_layout(model, "users", vec![form], empty![])
|
let users: Vec<Node<Msg>> = page
|
||||||
|
.invited_users
|
||||||
|
.iter()
|
||||||
|
.map(|user| {
|
||||||
|
let remove = StyledButton::build().text("Remove").build().into_node();
|
||||||
|
li![
|
||||||
|
class!["user"],
|
||||||
|
span![user.name],
|
||||||
|
span![user.email],
|
||||||
|
span![format!("{}", user.user_role)],
|
||||||
|
remove,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let users_section = section![
|
||||||
|
class!["usersSection"],
|
||||||
|
h1![class!["heading"], "Users"],
|
||||||
|
ul![class!["usersList"], users],
|
||||||
|
];
|
||||||
|
|
||||||
|
let invitations: Vec<Node<Msg>> = page
|
||||||
|
.invitations
|
||||||
|
.iter()
|
||||||
|
.map(|invitation| {
|
||||||
|
let revoke = StyledButton::build().text("Revoke").build().into_node();
|
||||||
|
li![
|
||||||
|
class!["invitation"],
|
||||||
|
span![invitation.name],
|
||||||
|
span![invitation.email],
|
||||||
|
span![format!("{}", invitation.state)],
|
||||||
|
revoke,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let invitations_section = section![
|
||||||
|
class!["invitationsSection"],
|
||||||
|
h1![class!["heading"], "Invitations"],
|
||||||
|
ul![class!["invitationsList"], invitations],
|
||||||
|
];
|
||||||
|
|
||||||
|
inner_layout(
|
||||||
|
model,
|
||||||
|
"users",
|
||||||
|
vec![form, users_section, invitations_section],
|
||||||
|
empty![],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ pub fn is_email(s: &str) -> bool {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_token(s: &str) -> bool {
|
pub fn is_token(s: &str) -> bool {
|
||||||
|
@ -64,7 +64,7 @@ pub fn exchange_position(issue_bellow_id: IssueId, model: &mut Model) {
|
|||||||
model.issues.push(c);
|
model.issues.push(c);
|
||||||
}
|
}
|
||||||
dragged.list_position = below.list_position + 1;
|
dragged.list_position = below.list_position + 1;
|
||||||
dragged.status = below.status.clone();
|
dragged.status = below.status;
|
||||||
}
|
}
|
||||||
std::mem::swap(&mut dragged.list_position, &mut below.list_position);
|
std::mem::swap(&mut dragged.list_position, &mut below.list_position);
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ pub fn sync(model: &mut Model) {
|
|||||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||||
issue.id,
|
issue.id,
|
||||||
IssueFieldId::Status,
|
IssueFieldId::Status,
|
||||||
PayloadVariant::IssueStatus(issue.status.clone()),
|
PayloadVariant::IssueStatus(issue.status),
|
||||||
));
|
));
|
||||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||||
issue.id,
|
issue.id,
|
||||||
@ -150,7 +150,6 @@ pub fn change_status(status: IssueStatus, model: &mut Model) {
|
|||||||
|
|
||||||
if issue.status == status {
|
if issue.status == status {
|
||||||
model.issues.push(issue);
|
model.issues.push(issue);
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
issue.status = status;
|
issue.status = status;
|
||||||
issue.list_position = pos + 1;
|
issue.list_position = pos + 1;
|
||||||
|
@ -29,7 +29,7 @@ pub type UsernameString = String;
|
|||||||
|
|
||||||
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
|
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
|
||||||
#[cfg_attr(feature = "backend", sql_type = "IssueTypeType")]
|
#[cfg_attr(feature = "backend", sql_type = "IssueTypeType")]
|
||||||
#[derive(Clone, Deserialize, Serialize, Debug, PartialOrd, PartialEq, Hash)]
|
#[derive(Clone, Copy, Deserialize, Serialize, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
pub enum IssueType {
|
pub enum IssueType {
|
||||||
Task,
|
Task,
|
||||||
Bug,
|
Bug,
|
||||||
@ -93,7 +93,7 @@ impl std::fmt::Display for IssueType {
|
|||||||
|
|
||||||
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
|
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
|
||||||
#[cfg_attr(feature = "backend", sql_type = "IssueStatusType")]
|
#[cfg_attr(feature = "backend", sql_type = "IssueStatusType")]
|
||||||
#[derive(Clone, Deserialize, Serialize, Debug, PartialOrd, PartialEq, Hash)]
|
#[derive(Clone, Copy, Deserialize, Serialize, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
pub enum IssueStatus {
|
pub enum IssueStatus {
|
||||||
Backlog,
|
Backlog,
|
||||||
Selected,
|
Selected,
|
||||||
|
@ -57,7 +57,7 @@ impl Handler<CreateInvitation> for DbExecutor {
|
|||||||
let conn = &self
|
let conn = &self
|
||||||
.pool
|
.pool
|
||||||
.get()
|
.get()
|
||||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||||
|
|
||||||
let form = InvitationForm {
|
let form = InvitationForm {
|
||||||
name: msg.name,
|
name: msg.name,
|
||||||
@ -70,7 +70,7 @@ impl Handler<CreateInvitation> for DbExecutor {
|
|||||||
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
||||||
query
|
query
|
||||||
.get_result(conn)
|
.get_result(conn)
|
||||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)
|
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ impl Handler<DeleteInvitation> for DbExecutor {
|
|||||||
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
||||||
query
|
query
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,7 +125,7 @@ impl Handler<RevokeInvitation> for DbExecutor {
|
|||||||
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
||||||
query
|
query
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,15 +154,22 @@ impl Handler<AcceptInvitation> for DbExecutor {
|
|||||||
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
||||||
let invitation: Invitation = query
|
let invitation: Invitation = query
|
||||||
.first(conn)
|
.first(conn)
|
||||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||||
|
|
||||||
|
if invitation.state == InvitationState::Revoked {
|
||||||
|
return Err(ServiceErrors::DatabaseQueryFailed(
|
||||||
|
"This invitation is no longer valid".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let query = diesel::update(invitations)
|
let query = diesel::update(invitations)
|
||||||
.set(state.eq(InvitationState::Accepted))
|
.set(state.eq(InvitationState::Accepted))
|
||||||
.filter(id.eq(invitation.id));
|
.filter(id.eq(invitation.id))
|
||||||
|
.filter(state.eq(InvitationState::Sent));
|
||||||
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
||||||
query
|
query
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||||
|
|
||||||
let form = UserForm {
|
let form = UserForm {
|
||||||
name: invitation.name,
|
name: invitation.name,
|
||||||
@ -174,7 +181,7 @@ impl Handler<AcceptInvitation> for DbExecutor {
|
|||||||
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
|
||||||
let user: User = query
|
let user: User = query
|
||||||
.get_result(conn)
|
.get_result(conn)
|
||||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
|
||||||
|
|
||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use actix::{Handler, Message};
|
use actix::{Handler, Message};
|
||||||
use lettre;
|
// use lettre;
|
||||||
use lettre_email;
|
// use lettre_email;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::mail::MailExecutor;
|
use crate::mail::MailExecutor;
|
||||||
@ -35,7 +35,7 @@ impl Handler<Invite> for MailExecutor {
|
|||||||
<p>
|
<p>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Please click this link: <a href="{addr}/invite?token={bind_token}"></a>
|
Please click this link: <a href="{addr}/invite?token={bind_token}">{addr}/invite?token={bind_token}</a>
|
||||||
</p>
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -46,7 +46,7 @@ impl Handler<Invite> for MailExecutor {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let email = lettre_email::Email::builder()
|
let email = lettre_email::Email::builder()
|
||||||
.from(from.clone())
|
.from(from)
|
||||||
.to(msg.email.as_str())
|
.to(msg.email.as_str())
|
||||||
.html(html.as_str())
|
.html(html.as_str())
|
||||||
.subject("Invitation to JIRS project")
|
.subject("Invitation to JIRS project")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::fs::*;
|
use std::fs::*;
|
||||||
|
|
||||||
use actix::{Actor, SyncContext};
|
use actix::{Actor, SyncContext};
|
||||||
use lettre;
|
// use lettre;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod invite;
|
pub mod invite;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use actix::{Handler, Message};
|
use actix::{Handler, Message};
|
||||||
use lettre;
|
// use lettre;
|
||||||
use lettre_email;
|
// use lettre_email;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::mail::MailExecutor;
|
use crate::mail::MailExecutor;
|
||||||
@ -45,7 +45,7 @@ impl Handler<Welcome> for MailExecutor {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let email = lettre_email::Email::builder()
|
let email = lettre_email::Email::builder()
|
||||||
.from(from.clone())
|
.from(from)
|
||||||
.to(msg.email.as_str())
|
.to(msg.email.as_str())
|
||||||
.html(html.as_str())
|
.html(html.as_str())
|
||||||
.subject("Welcome to JIRS")
|
.subject("Welcome to JIRS")
|
||||||
|
@ -43,7 +43,7 @@ impl WsHandler<Authenticate> for WebSocketActor {
|
|||||||
if let Some(bind_token) = token.bind_token.as_ref().cloned() {
|
if let Some(bind_token) = token.bind_token.as_ref().cloned() {
|
||||||
match block_on(self.mail.send(Welcome {
|
match block_on(self.mail.send(Welcome {
|
||||||
bind_token,
|
bind_token,
|
||||||
email: user.email.clone(),
|
email: user.email,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(_)) => (),
|
Ok(Ok(_)) => (),
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
@ -69,7 +69,7 @@ impl WsHandler<CheckAuthToken> for WebSocketActor {
|
|||||||
let user: jirs_data::User = match block_on(self.db.send(AuthorizeUser {
|
let user: jirs_data::User = match block_on(self.db.send(AuthorizeUser {
|
||||||
access_token: msg.token,
|
access_token: msg.token,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(u)) => u.into(),
|
Ok(Ok(u)) => u,
|
||||||
Ok(Err(_)) => {
|
Ok(Err(_)) => {
|
||||||
return Ok(Some(WsMsg::AuthorizeLoaded(Err(
|
return Ok(Some(WsMsg::AuthorizeLoaded(Err(
|
||||||
"Invalid auth token".to_string()
|
"Invalid auth token".to_string()
|
||||||
|
@ -15,7 +15,7 @@ impl WsHandler<LoadIssueComments> for WebSocketActor {
|
|||||||
let comments = match block_on(self.db.send(crate::db::comments::LoadIssueComments {
|
let comments = match block_on(self.db.send(crate::db::comments::LoadIssueComments {
|
||||||
issue_id: msg.issue_id,
|
issue_id: msg.issue_id,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(comments)) => comments.into_iter().map(|c| c.into()).collect(),
|
Ok(Ok(comments)) => comments,
|
||||||
_ => return Ok(None),
|
_ => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,15 +45,29 @@ impl WsHandler<CreateInvitation> for WebSocketActor {
|
|||||||
name,
|
name,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(invitation)) => invitation,
|
Ok(Ok(invitation)) => invitation,
|
||||||
_ => return Ok(Some(WsMsg::InvitationSendFailure)),
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(Some(WsMsg::InvitationSendFailure));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(Some(WsMsg::InvitationSendFailure));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match block_on(self.mail.send(crate::mail::invite::Invite {
|
match block_on(self.mail.send(crate::mail::invite::Invite {
|
||||||
bind_token: invitation.bind_token.clone(),
|
bind_token: invitation.bind_token,
|
||||||
email: invitation.email.clone(),
|
email: invitation.email,
|
||||||
inviter_name,
|
inviter_name,
|
||||||
})) {
|
})) {
|
||||||
Ok(Ok(_)) => (),
|
Ok(Ok(_)) => (),
|
||||||
_ => return Ok(Some(WsMsg::InvitationSendFailure)),
|
Ok(Err(e)) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return Ok(Some(WsMsg::InvitationSendFailure));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{}", e);
|
||||||
|
return Ok(Some(WsMsg::InvitationSendFailure));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(WsMsg::InvitationSendSuccess))
|
Ok(Some(WsMsg::InvitationSendSuccess))
|
||||||
|
@ -9,9 +9,12 @@ use jirs_data::{ProjectId, UserId, WsMsg};
|
|||||||
|
|
||||||
use crate::db::DbExecutor;
|
use crate::db::DbExecutor;
|
||||||
use crate::mail::MailExecutor;
|
use crate::mail::MailExecutor;
|
||||||
use crate::ws::auth::{Authenticate, CheckAuthToken, CheckBindToken};
|
use crate::ws::auth::*;
|
||||||
|
use crate::ws::comments::*;
|
||||||
use crate::ws::invitations::*;
|
use crate::ws::invitations::*;
|
||||||
use crate::ws::issues::UpdateIssueHandler;
|
use crate::ws::issues::*;
|
||||||
|
use crate::ws::projects::*;
|
||||||
|
use crate::ws::users::*;
|
||||||
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod comments;
|
pub mod comments;
|
||||||
@ -47,9 +50,8 @@ impl Handler<InnerMsg> for WebSocketActor {
|
|||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: InnerMsg, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: InnerMsg, ctx: &mut Self::Context) -> Self::Result {
|
||||||
match msg {
|
if let InnerMsg::Transfer(msg) = msg {
|
||||||
InnerMsg::Transfer(msg) => ctx.send_msg(&msg),
|
ctx.send_msg(&msg)
|
||||||
_ => {}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,11 +89,11 @@ impl WebSocketActor {
|
|||||||
ctx,
|
ctx,
|
||||||
)?,
|
)?,
|
||||||
WsMsg::IssueCreateRequest(payload) => self.handle_msg(payload, ctx)?,
|
WsMsg::IssueCreateRequest(payload) => self.handle_msg(payload, ctx)?,
|
||||||
WsMsg::IssueDeleteRequest(id) => self.handle_msg(issues::DeleteIssue { id }, ctx)?,
|
WsMsg::IssueDeleteRequest(id) => self.handle_msg(DeleteIssue { id }, ctx)?,
|
||||||
WsMsg::ProjectIssuesRequest => self.handle_msg(issues::LoadIssues, ctx)?,
|
WsMsg::ProjectIssuesRequest => self.handle_msg(LoadIssues, ctx)?,
|
||||||
|
|
||||||
// projects
|
// projects
|
||||||
WsMsg::ProjectRequest => self.handle_msg(projects::CurrentProject, ctx)?,
|
WsMsg::ProjectRequest => self.handle_msg(CurrentProject, ctx)?,
|
||||||
WsMsg::ProjectUpdateRequest(payload) => self.handle_msg(payload, ctx)?,
|
WsMsg::ProjectUpdateRequest(payload) => self.handle_msg(payload, ctx)?,
|
||||||
|
|
||||||
// auth
|
// auth
|
||||||
@ -107,7 +109,7 @@ impl WebSocketActor {
|
|||||||
|
|
||||||
// register
|
// register
|
||||||
WsMsg::SignUpRequest(email, username) => self.handle_msg(
|
WsMsg::SignUpRequest(email, username) => self.handle_msg(
|
||||||
users::Register {
|
Register {
|
||||||
name: username,
|
name: username,
|
||||||
email,
|
email,
|
||||||
},
|
},
|
||||||
@ -115,32 +117,26 @@ impl WebSocketActor {
|
|||||||
)?,
|
)?,
|
||||||
|
|
||||||
// users
|
// users
|
||||||
WsMsg::ProjectUsersRequest => self.handle_msg(users::LoadProjectUsers, ctx)?,
|
WsMsg::ProjectUsersRequest => self.handle_msg(LoadProjectUsers, ctx)?,
|
||||||
|
|
||||||
// comments
|
// comments
|
||||||
WsMsg::IssueCommentsRequest(issue_id) => {
|
WsMsg::IssueCommentsRequest(issue_id) => {
|
||||||
self.handle_msg(comments::LoadIssueComments { issue_id }, ctx)?
|
self.handle_msg(LoadIssueComments { issue_id }, ctx)?
|
||||||
}
|
}
|
||||||
WsMsg::CreateComment(payload) => self.handle_msg(payload, ctx)?,
|
WsMsg::CreateComment(payload) => self.handle_msg(payload, ctx)?,
|
||||||
WsMsg::UpdateComment(payload) => self.handle_msg(payload, ctx)?,
|
WsMsg::UpdateComment(payload) => self.handle_msg(payload, ctx)?,
|
||||||
WsMsg::CommentDeleteRequest(comment_id) => {
|
WsMsg::CommentDeleteRequest(comment_id) => {
|
||||||
self.handle_msg(comments::DeleteComment { comment_id }, ctx)?
|
self.handle_msg(DeleteComment { comment_id }, ctx)?
|
||||||
}
|
}
|
||||||
|
|
||||||
// invitations
|
// invitations
|
||||||
WsMsg::InvitationSendRequest { name, email } => self.handle_msg(
|
WsMsg::InvitationSendRequest { name, email } => {
|
||||||
CreateInvitation {
|
self.handle_msg(CreateInvitation { name, email }, ctx)?
|
||||||
name: name.clone(),
|
}
|
||||||
email: email.clone(),
|
|
||||||
},
|
|
||||||
ctx,
|
|
||||||
)?,
|
|
||||||
WsMsg::InvitationListRequest => self.handle_msg(ListInvitation, ctx)?,
|
WsMsg::InvitationListRequest => self.handle_msg(ListInvitation, ctx)?,
|
||||||
WsMsg::InvitationAcceptRequest(id) => self.handle_msg(AcceptInvitation { id }, ctx)?,
|
WsMsg::InvitationAcceptRequest(id) => self.handle_msg(AcceptInvitation { id }, 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::InvitedUsersRequest => None,
|
|
||||||
|
|
||||||
// else fail
|
// else fail
|
||||||
_ => {
|
_ => {
|
||||||
@ -296,9 +292,7 @@ impl Handler<InnerMsg> for WsServer {
|
|||||||
|
|
||||||
impl WsServer {
|
impl WsServer {
|
||||||
pub fn ensure_room(&mut self, room: i32) {
|
pub fn ensure_room(&mut self, room: i32) {
|
||||||
if !self.rooms.contains_key(&room) {
|
self.rooms.entry(room).or_insert_with(HashSet::new);
|
||||||
self.rooms.insert(room, HashSet::new());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ impl WsHandler<CurrentProject> for WebSocketActor {
|
|||||||
let project_id = self.require_user()?.project_id;
|
let project_id = self.require_user()?.project_id;
|
||||||
|
|
||||||
let m = match block_on(self.db.send(LoadCurrentProject { project_id })) {
|
let m = match block_on(self.db.send(LoadCurrentProject { project_id })) {
|
||||||
Ok(Ok(project)) => Some(WsMsg::ProjectLoaded(project.into())),
|
Ok(Ok(project)) => Some(WsMsg::ProjectLoaded(project)),
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
error!("{:?}", e);
|
error!("{:?}", e);
|
||||||
None
|
None
|
||||||
@ -39,6 +39,6 @@ impl WsHandler<UpdateProjectPayload> for WebSocketActor {
|
|||||||
Ok(Ok(project)) => project,
|
Ok(Ok(project)) => project,
|
||||||
_ => return Ok(None),
|
_ => return Ok(None),
|
||||||
};
|
};
|
||||||
Ok(Some(WsMsg::ProjectLoaded(project.into())))
|
Ok(Some(WsMsg::ProjectLoaded(project)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,7 @@ impl WsHandler<LoadProjectUsers> for WebSocketActor {
|
|||||||
|
|
||||||
let project_id = self.require_user()?.project_id;
|
let project_id = self.require_user()?.project_id;
|
||||||
let m = match block_on(self.db.send(Msg { project_id })) {
|
let m = match block_on(self.db.send(Msg { project_id })) {
|
||||||
Ok(Ok(v)) => Some(WsMsg::ProjectUsersLoaded(
|
Ok(Ok(v)) => Some(WsMsg::ProjectUsersLoaded(v)),
|
||||||
v.into_iter().map(|i| i.into()).collect(),
|
|
||||||
)),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
Ok(m)
|
Ok(m)
|
||||||
|
Loading…
Reference in New Issue
Block a user