Accept invitation and redirect. Render is broken

This commit is contained in:
Adrian Wozniak 2020-05-22 21:53:37 +02:00
parent 3cb74084d9
commit 3ccafecfb9
6 changed files with 112 additions and 38 deletions

View File

@ -18,3 +18,8 @@
#invite > .styledForm:last-of-type {
box-shadow: rgba(0, 0, 0, 0.1) 0 10px 10px;
}
#invite .error {
color: var(--danger);
margin-top: 15px;
}

View File

@ -9,7 +9,7 @@ use crate::shared::styled_button::StyledButton;
use crate::shared::styled_field::StyledField;
use crate::shared::styled_form::StyledForm;
use crate::shared::styled_input::StyledInput;
use crate::shared::{outer_layout, ToNode};
use crate::shared::{go_to_board, outer_layout, write_auth_token, ToNode};
use crate::validations::is_token;
use crate::ws::send_ws_msg;
use crate::{FieldId, InvitationPageChange, Msg, PageChanged, WebSocketChanged};
@ -27,12 +27,21 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
};
match msg {
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::InvitationAcceptFailure(_))) => {
page.error = Some("Invalid token".to_string());
}
Msg::WebSocketChange(WebSocketChanged::WsMsg(ws_msg)) => match ws_msg {
WsMsg::InvitationAcceptFailure(_) => {
page.error = Some("Invalid token".to_string());
}
WsMsg::InvitationAcceptSuccess(token) => {
if let Ok(_) = write_auth_token(Some(token)) {
go_to_board(orders);
}
}
_ => (),
},
Msg::StrInputChanged(FieldId::Invite(InviteFieldId::Token), text) => {
page.token_touched = true;
page.token = text;
page.error = None;
}
Msg::PageChanged(PageChanged::Invitation(InvitationPageChange::SubmitForm)) => {
if let Ok(token) = uuid::Uuid::from_str(page.token.as_str()) {
@ -97,10 +106,11 @@ fn submit(_page: &Box<InvitePage>) -> Node<Msg> {
fn token_field(page: &Box<InvitePage>) -> Node<Msg> {
let token = StyledInput::build(FieldId::Invite(InviteFieldId::Token))
.valid(!page.token_touched || is_token(page.token.as_str()))
.valid(!page.token_touched || is_token(page.token.as_str()) && page.error.is_none())
.value(page.token.as_str())
.build()
.into_node();
StyledField::build()
.input(token)
.label("Your invite token")

View File

@ -7,6 +7,8 @@ use jirs_data::{
User, UserId, UserRole, UsernameString,
};
use crate::db::tokens::CreateBindToken;
use crate::db::users::{FindUser, Register};
use crate::db::DbExecutor;
use crate::errors::ServiceErrors;
@ -149,7 +151,7 @@ impl Message for AcceptInvitation {
impl Handler<AcceptInvitation> for DbExecutor {
type Result = Result<Token, ServiceErrors>;
fn handle(&mut self, msg: AcceptInvitation, _ctx: &mut Self::Context) -> Self::Result {
fn handle(&mut self, msg: AcceptInvitation, ctx: &mut Self::Context) -> Self::Result {
use crate::schema::invitations::dsl::*;
let conn = &self
@ -177,21 +179,32 @@ impl Handler<AcceptInvitation> for DbExecutor {
.filter(id.eq(invitation.id))
.filter(state.eq(InvitationState::Sent));
debug!("{}", diesel::debug_query::<Pg, _>(&query).to_string());
query
.execute(conn)
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?;
query.execute(conn).map_err(|e| {
ServiceErrors::DatabaseQueryFailed(format!("update invitation {} {}", invitation.id, e))
})?;
let user: User = {
use crate::schema::users::dsl::*;
let query = users
.filter(name.eq(invitation.name).and(email.eq(invitation.email)))
.limit(1);
debug!("{}", diesel::debug_query::<Pg, _>(&query));
query
.first(conn)
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?
match self.handle(
Register {
name: invitation.name.clone(),
email: invitation.email.clone(),
project_id: Some(invitation.project_id),
},
ctx,
) {
Ok(_) => (),
Err(ServiceErrors::RegisterCollision) => (),
Err(e) => return Err(e),
};
let user: User = self.handle(
FindUser {
name: invitation.name.clone(),
email: invitation.email.clone(),
},
ctx,
)?;
self.handle(CreateBindToken { user_id: user.id }, ctx)?;
{
use crate::schema::user_projects::dsl::*;
@ -208,11 +221,12 @@ impl Handler<AcceptInvitation> for DbExecutor {
let token = {
use crate::schema::tokens::dsl::*;
let query = tokens.filter(user_id.eq(user.id));
let query = tokens.filter(user_id.eq(user.id)).order_by(id.desc());
debug!("{}", diesel::debug_query::<Pg, _>(&query));
query
.first(conn)
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))?
query.first(conn).map_err(|e| {
ServiceErrors::DatabaseQueryFailed(format!("token for user {} {}", user.id, e))
})?
};
Ok(token)

View File

@ -41,6 +41,45 @@ impl Handler<LoadCurrentProject> for DbExecutor {
}
}
#[derive(Serialize, Deserialize)]
pub struct CreateProject {
pub name: String,
pub url: Option<String>,
pub description: Option<String>,
pub category: Option<ProjectCategory>,
pub time_tracking: Option<TimeTracking>,
}
impl Message for CreateProject {
type Result = Result<Project, ServiceErrors>;
}
impl Handler<CreateProject> for DbExecutor {
type Result = Result<Project, ServiceErrors>;
fn handle(&mut self, msg: CreateProject, _ctx: &mut Self::Context) -> Self::Result {
use crate::schema::projects::dsl::*;
let conn = &self
.pool
.get()
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
let query = diesel::insert_into(projects)
.values((
name.eq(msg.name),
msg.url.map(|v| url.eq(v)),
msg.description.map(|v| description.eq(v)),
msg.category.map(|v| category.eq(v)),
msg.time_tracking.map(|v| time_tracking.eq(v)),
))
.returning(all_columns);
debug!("{}", diesel::debug_query::<Pg, _>(&query));
query
.get_result::<Project>(conn)
.map_err(|e| ServiceErrors::DatabaseQueryFailed(format!("{}", e)))
}
}
#[derive(Serialize, Deserialize)]
pub struct UpdateProject {
pub project_id: i32,

View File

@ -3,11 +3,11 @@ use diesel::pg::Pg;
use diesel::prelude::*;
use serde::{Deserialize, Serialize};
use jirs_data::{Project, User, UserId};
use jirs_data::{ProjectId, User, UserId};
use crate::db::projects::CreateProject;
use crate::db::{DbExecutor, DbPooledConn};
use crate::errors::ServiceErrors;
use crate::models::CreateProjectForm;
use crate::schema::users::all_columns;
#[derive(Serialize, Deserialize, Debug)]
@ -112,6 +112,7 @@ impl Handler<LoadIssueAssignees> for DbExecutor {
pub struct Register {
pub name: String,
pub email: String,
pub project_id: Option<ProjectId>,
}
impl Message for Register {
@ -121,8 +122,7 @@ impl Message for Register {
impl Handler<Register> for DbExecutor {
type Result = Result<(), ServiceErrors>;
fn handle(&mut self, msg: Register, _ctx: &mut Self::Context) -> Self::Result {
use crate::schema::projects::dsl::projects;
fn handle(&mut self, msg: Register, ctx: &mut Self::Context) -> Self::Result {
use crate::schema::users::dsl::*;
let conn = &self
@ -136,17 +136,22 @@ impl Handler<Register> for DbExecutor {
return Err(ServiceErrors::RegisterCollision);
}
let form = CreateProjectForm {
name: "initial".to_string(),
url: "".to_string(),
description: "".to_string(),
category: Default::default(),
let current_project_id: ProjectId = match msg.project_id.as_ref().cloned() {
Some(current_project_id) => current_project_id,
_ => {
self.handle(
CreateProject {
name: "initial".to_string(),
url: None,
description: None,
category: None,
time_tracking: None,
},
ctx,
)?
.id
}
};
let insert_query = diesel::insert_into(projects).values(form);
debug!("{}", diesel::debug_query::<Pg, _>(&insert_query));
let project: Project = insert_query
.get_result(conn)
.map_err(|_| ServiceErrors::RegisterCollision)?;
let user: User = {
let insert_user_query =
@ -161,7 +166,7 @@ impl Handler<Register> for DbExecutor {
use crate::schema::user_projects::dsl::*;
let insert_user_project_query = diesel::insert_into(user_projects).values((
user_id.eq(user.id),
project_id.eq(project.id),
project_id.eq(current_project_id),
is_current.eq(true),
is_default.eq(true),
));

View File

@ -40,6 +40,7 @@ impl WsHandler<Register> for WebSocketActor {
let msg = match block_on(self.db.send(DbRegister {
name: name.clone(),
email: email.clone(),
project_id: None,
})) {
Ok(Ok(_)) => Some(WsMsg::SignUpSuccess),
Ok(Err(_)) => Some(WsMsg::SignUpPairTaken),