Destroy invitation message after accepting invitation
This commit is contained in:
parent
d3b2fbce32
commit
9b6bfc76cb
@ -113,13 +113,13 @@ nav#sidebar .linkItem > a > .linkText {
|
||||
|
||||
.styledTooltip.messages > .messagesList > .message {
|
||||
padding: 15px;
|
||||
max-height: 90px;
|
||||
overflow: hidden;
|
||||
/*max-height: 90px;*/
|
||||
/*overflow: hidden;*/
|
||||
}
|
||||
|
||||
.styledTooltip.messages > .messagesList > .message:hover {
|
||||
max-height: 100%;
|
||||
}
|
||||
/*.styledTooltip.messages > .messagesList > .message:hover {*/
|
||||
/* max-height: 100%;*/
|
||||
/*}*/
|
||||
|
||||
.styledTooltip.messages > .messagesList > .message > .top {
|
||||
display: flex;
|
||||
|
@ -213,8 +213,21 @@ pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
}
|
||||
// messages
|
||||
WsMsg::Message(received) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut old, &mut model.messages);
|
||||
for m in old {
|
||||
if m.id != received.id {
|
||||
model.messages.push(m);
|
||||
} else {
|
||||
model.messages.push(received.clone());
|
||||
}
|
||||
}
|
||||
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
}
|
||||
WsMsg::MessagesResponse(v) => {
|
||||
model.messages = v.clone();
|
||||
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
}
|
||||
WsMsg::MessageMarkedSeen(id) => {
|
||||
let mut old = vec![];
|
||||
@ -224,6 +237,7 @@ pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
model.messages.push(m);
|
||||
}
|
||||
}
|
||||
model.messages.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
|
@ -9,7 +9,7 @@ use jirs_data::{
|
||||
};
|
||||
|
||||
use crate::db::tokens::CreateBindToken;
|
||||
use crate::db::users::{FindUser, Register};
|
||||
use crate::db::users::{LookupUser, Register};
|
||||
use crate::db::DbExecutor;
|
||||
use crate::errors::ServiceErrors;
|
||||
|
||||
@ -236,7 +236,7 @@ impl Handler<AcceptInvitation> for DbExecutor {
|
||||
};
|
||||
|
||||
let user: User = self.handle(
|
||||
FindUser {
|
||||
LookupUser {
|
||||
name: invitation.name.clone(),
|
||||
email: invitation.email.clone(),
|
||||
},
|
||||
|
@ -1,11 +1,13 @@
|
||||
use actix::Handler;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use jirs_data::{Message, MessageId, UserId};
|
||||
use jirs_data::{BindToken, Message, MessageId, MessageType, User, UserId};
|
||||
|
||||
use crate::db::users::{FindUser, LookupUser};
|
||||
use crate::db::DbExecutor;
|
||||
use crate::errors::ServiceErrors;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LoadMessages {
|
||||
pub user_id: UserId,
|
||||
}
|
||||
@ -36,6 +38,7 @@ impl Handler<LoadMessages> for DbExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MarkMessageSeen {
|
||||
pub user_id: UserId,
|
||||
pub message_id: MessageId,
|
||||
@ -61,13 +64,119 @@ impl Handler<MarkMessageSeen> for DbExecutor {
|
||||
.find(msg.message_id)
|
||||
.filter(receiver_id.eq(msg.user_id)),
|
||||
);
|
||||
debug!("{}", diesel::debug_query::<diesel::pg::Pg, _>(&query));
|
||||
let size = query
|
||||
.execute(conn)
|
||||
.map_err(|_| ServiceErrors::DatabaseQueryFailed("load user messages".to_string()))?;
|
||||
|
||||
if size > 0 {
|
||||
Ok(msg.message_id)
|
||||
} else {
|
||||
Err(ServiceErrors::DatabaseQueryFailed(format!(
|
||||
"failed to delete message for {:?}",
|
||||
msg
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CreateMessageReceiver {
|
||||
Reference(UserId),
|
||||
Lookup { name: String, email: String },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CreateMessage {
|
||||
pub receiver: CreateMessageReceiver,
|
||||
pub sender_id: UserId,
|
||||
pub summary: String,
|
||||
pub description: String,
|
||||
pub message_type: MessageType,
|
||||
pub hyper_link: String,
|
||||
}
|
||||
|
||||
impl actix::Message for CreateMessage {
|
||||
type Result = Result<Message, ServiceErrors>;
|
||||
}
|
||||
|
||||
impl Handler<CreateMessage> for DbExecutor {
|
||||
type Result = Result<Message, ServiceErrors>;
|
||||
|
||||
fn handle(&mut self, msg: CreateMessage, ctx: &mut Self::Context) -> Self::Result {
|
||||
use crate::schema::messages::dsl::*;
|
||||
|
||||
let conn = &self
|
||||
.pool
|
||||
.get()
|
||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||
|
||||
let user: User = match {
|
||||
match msg.receiver {
|
||||
CreateMessageReceiver::Lookup { name, email } => {
|
||||
self.handle(LookupUser { name, email }, ctx)
|
||||
}
|
||||
CreateMessageReceiver::Reference(user_id) => self.handle(FindUser { user_id }, ctx),
|
||||
}
|
||||
} {
|
||||
Ok(user) => user,
|
||||
_ => {
|
||||
return Err(ServiceErrors::RecordNotFound(
|
||||
"No matching user found".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let query = diesel::insert_into(messages).values((
|
||||
receiver_id.eq(user.id),
|
||||
sender_id.eq(msg.sender_id),
|
||||
summary.eq(msg.summary),
|
||||
description.eq(msg.description),
|
||||
message_type.eq(msg.message_type),
|
||||
hyper_link.eq(msg.hyper_link),
|
||||
));
|
||||
debug!(
|
||||
"{}",
|
||||
diesel::debug_query::<diesel::pg::Pg, _>(&query).to_string()
|
||||
);
|
||||
query
|
||||
.execute(conn)
|
||||
.map_err(|_| ServiceErrors::DatabaseQueryFailed("load user messages".to_string()))?;
|
||||
Ok(msg.message_id)
|
||||
.get_result(conn)
|
||||
.map_err(|_| ServiceErrors::DatabaseQueryFailed("create message failed".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LookupMessagesByToken {
|
||||
pub token: BindToken,
|
||||
pub user_id: UserId,
|
||||
}
|
||||
|
||||
impl actix::Message for LookupMessagesByToken {
|
||||
type Result = Result<Vec<Message>, ServiceErrors>;
|
||||
}
|
||||
|
||||
impl Handler<LookupMessagesByToken> for DbExecutor {
|
||||
type Result = Result<Vec<Message>, ServiceErrors>;
|
||||
|
||||
fn handle(&mut self, msg: LookupMessagesByToken, _ctx: &mut Self::Context) -> Self::Result {
|
||||
use crate::schema::messages::dsl::*;
|
||||
|
||||
let conn = &self
|
||||
.pool
|
||||
.get()
|
||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||
|
||||
let query = messages.filter(
|
||||
hyper_link
|
||||
.eq(format!("#{}", msg.token))
|
||||
.and(receiver_id.eq(msg.user_id)),
|
||||
);
|
||||
debug!(
|
||||
"{}",
|
||||
diesel::debug_query::<diesel::pg::Pg, _>(&query).to_string()
|
||||
);
|
||||
query
|
||||
.load(conn)
|
||||
.map_err(|_| ServiceErrors::DatabaseQueryFailed("create message failed".to_string()))
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use actix::{Handler, Message};
|
||||
use diesel::connection::TransactionManager;
|
||||
use diesel::pg::Pg;
|
||||
use diesel::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -9,12 +10,10 @@ use crate::db::projects::CreateProject;
|
||||
use crate::db::{DbExecutor, DbPooledConn};
|
||||
use crate::errors::ServiceErrors;
|
||||
use crate::schema::users::all_columns;
|
||||
use diesel::connection::TransactionManager;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct FindUser {
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
pub user_id: UserId,
|
||||
}
|
||||
|
||||
impl Message for FindUser {
|
||||
@ -27,6 +26,35 @@ impl Handler<FindUser> for DbExecutor {
|
||||
fn handle(&mut self, msg: FindUser, _ctx: &mut Self::Context) -> Self::Result {
|
||||
use crate::schema::users::dsl::*;
|
||||
|
||||
let conn = &self
|
||||
.pool
|
||||
.get()
|
||||
.map_err(|_| ServiceErrors::DatabaseConnectionLost)?;
|
||||
|
||||
let query = users.find(msg.user_id);
|
||||
debug!("{}", diesel::debug_query::<Pg, _>(&query));
|
||||
query
|
||||
.first(conn)
|
||||
.map_err(|_| ServiceErrors::RecordNotFound(format!("user with id = {}", msg.user_id)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LookupUser {
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
}
|
||||
|
||||
impl Message for LookupUser {
|
||||
type Result = Result<User, ServiceErrors>;
|
||||
}
|
||||
|
||||
impl Handler<LookupUser> for DbExecutor {
|
||||
type Result = Result<User, ServiceErrors>;
|
||||
|
||||
fn handle(&mut self, msg: LookupUser, _ctx: &mut Self::Context) -> Self::Result {
|
||||
use crate::schema::users::dsl::*;
|
||||
|
||||
let conn = &self
|
||||
.pool
|
||||
.get()
|
||||
@ -43,7 +71,7 @@ impl Handler<FindUser> for DbExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug)]
|
||||
pub struct LoadProjectUsers {
|
||||
pub project_id: i32,
|
||||
}
|
||||
@ -76,7 +104,7 @@ impl Handler<LoadProjectUsers> for DbExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug)]
|
||||
pub struct LoadIssueAssignees {
|
||||
pub issue_id: i32,
|
||||
}
|
||||
@ -109,7 +137,7 @@ impl Handler<LoadIssueAssignees> for DbExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct Register {
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
@ -309,11 +337,13 @@ impl Handler<ProfileUpdate> for DbExecutor {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use diesel::connection::TransactionManager;
|
||||
|
||||
use jirs_data::{Project, ProjectCategory};
|
||||
|
||||
use crate::db::build_pool;
|
||||
|
||||
use super::*;
|
||||
use diesel::connection::TransactionManager;
|
||||
use jirs_data::{Project, ProjectCategory};
|
||||
|
||||
#[test]
|
||||
fn check_collision() {
|
||||
|
@ -5,7 +5,7 @@ use jirs_data::{Token, WsMsg};
|
||||
|
||||
use crate::db::authorize_user::AuthorizeUser;
|
||||
use crate::db::tokens::{CreateBindToken, FindBindToken};
|
||||
use crate::db::users::FindUser;
|
||||
use crate::db::users::LookupUser;
|
||||
use crate::mail::welcome::Welcome;
|
||||
use crate::ws::{WebSocketActor, WsHandler, WsResult};
|
||||
|
||||
@ -18,7 +18,7 @@ impl WsHandler<Authenticate> for WebSocketActor {
|
||||
fn handle_msg(&mut self, msg: Authenticate, _ctx: &mut Self::Context) -> WsResult {
|
||||
let Authenticate { name, email } = msg;
|
||||
// TODO check attempt number, allow only 5 times per day
|
||||
let user = match block_on(self.db.send(FindUser { name, email })) {
|
||||
let user = match block_on(self.db.send(LookupUser { name, email })) {
|
||||
Ok(Ok(user)) => user,
|
||||
Ok(Err(e)) => {
|
||||
error!("{:?}", e);
|
||||
|
@ -1,9 +1,12 @@
|
||||
use futures::executor::block_on;
|
||||
|
||||
use jirs_data::{EmailString, InvitationId, InvitationToken, UserRole, UsernameString, WsMsg};
|
||||
use jirs_data::{
|
||||
EmailString, InvitationId, InvitationToken, MessageType, UserRole, UsernameString, WsMsg,
|
||||
};
|
||||
|
||||
use crate::db::invitations;
|
||||
use crate::ws::{WebSocketActor, WsHandler, WsResult};
|
||||
use crate::db::messages::CreateMessageReceiver;
|
||||
use crate::ws::{InnerMsg, WebSocketActor, WsHandler, WsMessageSender, WsResult};
|
||||
|
||||
pub struct ListInvitation;
|
||||
|
||||
@ -40,18 +43,14 @@ impl WsHandler<CreateInvitation> for WebSocketActor {
|
||||
Some(up) => up.project_id,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
let (user_id, inviter_name) =
|
||||
match self.current_user.as_ref().map(|u| (u.id, u.name.clone())) {
|
||||
Some(id) => id,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
let (user_id, inviter_name) = self.require_user().map(|u| (u.id, u.name.clone()))?;
|
||||
|
||||
let CreateInvitation { email, name, role } = msg;
|
||||
let invitation = match block_on(self.db.send(invitations::CreateInvitation {
|
||||
let invitation = match block_on(self.db.send(crate::db::invitations::CreateInvitation {
|
||||
user_id,
|
||||
project_id,
|
||||
email,
|
||||
name,
|
||||
email: email.clone(),
|
||||
name: name.clone(),
|
||||
role,
|
||||
})) {
|
||||
Ok(Ok(invitation)) => invitation,
|
||||
@ -80,6 +79,24 @@ impl WsHandler<CreateInvitation> for WebSocketActor {
|
||||
}
|
||||
}
|
||||
|
||||
// If user exists then send message to him
|
||||
match block_on(self.db.send(crate::db::messages::CreateMessage {
|
||||
receiver: CreateMessageReceiver::Lookup { name, email },
|
||||
sender_id: user_id,
|
||||
summary: "You have been invited to project".to_string(),
|
||||
description: "You have been invited to project".to_string(),
|
||||
message_type: MessageType::ReceivedInvitation,
|
||||
hyper_link: format!("#{}", invitation.bind_token),
|
||||
})) {
|
||||
Ok(Ok(message)) => {
|
||||
self.addr.do_send(InnerMsg::SendToUser(
|
||||
message.receiver_id,
|
||||
WsMsg::Message(message),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(Some(WsMsg::InvitationSendSuccess))
|
||||
}
|
||||
}
|
||||
@ -135,21 +152,45 @@ pub struct AcceptInvitation {
|
||||
}
|
||||
|
||||
impl WsHandler<AcceptInvitation> for WebSocketActor {
|
||||
fn handle_msg(&mut self, msg: AcceptInvitation, _ctx: &mut Self::Context) -> WsResult {
|
||||
fn handle_msg(&mut self, msg: AcceptInvitation, ctx: &mut Self::Context) -> WsResult {
|
||||
let AcceptInvitation { invitation_token } = msg;
|
||||
let res = match block_on(self.db.send(invitations::AcceptInvitation {
|
||||
let token = match block_on(self.db.send(invitations::AcceptInvitation {
|
||||
invitation_token: invitation_token.clone(),
|
||||
})) {
|
||||
Ok(Ok(token)) => Some(WsMsg::InvitationAcceptSuccess(token.access_token)),
|
||||
Ok(Ok(token)) => token,
|
||||
Ok(Err(e)) => {
|
||||
error!("{:?}", e);
|
||||
Some(WsMsg::InvitationAcceptFailure(invitation_token))
|
||||
return Ok(Some(WsMsg::InvitationAcceptFailure(invitation_token)));
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
Some(WsMsg::InvitationAcceptFailure(invitation_token))
|
||||
return Ok(Some(WsMsg::InvitationAcceptFailure(invitation_token)));
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
|
||||
for message in block_on(self.db.send(crate::db::messages::LookupMessagesByToken {
|
||||
token: invitation_token,
|
||||
user_id: token.user_id,
|
||||
}))
|
||||
.unwrap_or(Ok(vec![]))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
match block_on(self.db.send(crate::db::messages::MarkMessageSeen {
|
||||
user_id: token.user_id,
|
||||
message_id: message.id,
|
||||
})) {
|
||||
Ok(Ok(id)) => {
|
||||
ctx.send_msg(&WsMsg::MessageMarkedSeen(id));
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
error!("{:?}", e);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(WsMsg::InvitationAcceptSuccess(token.access_token)))
|
||||
}
|
||||
}
|
||||
|
@ -325,6 +325,7 @@ pub enum InnerMsg {
|
||||
Join(ProjectId, UserId, Recipient<InnerMsg>),
|
||||
Leave(ProjectId, UserId, Recipient<InnerMsg>),
|
||||
BroadcastToChannel(ProjectId, WsMsg),
|
||||
SendToUser(UserId, WsMsg),
|
||||
Transfer(WsMsg),
|
||||
}
|
||||
|
||||
@ -381,13 +382,17 @@ impl Handler<InnerMsg> for WsServer {
|
||||
v.remove_item(&recipient);
|
||||
}
|
||||
}
|
||||
InnerMsg::SendToUser(user_id, msg) => {
|
||||
if let Some(v) = self.sessions.get(&user_id) {
|
||||
self.send_to_recipients(v, &msg);
|
||||
}
|
||||
}
|
||||
InnerMsg::BroadcastToChannel(project_id, msg) => {
|
||||
debug!("Begin broadcast to channel {} msg {:?}", project_id, msg);
|
||||
let set = match self.rooms.get(&project_id) {
|
||||
Some(s) => s,
|
||||
_ => return debug!(" channel not found, aborting..."),
|
||||
};
|
||||
let _s = set.len();
|
||||
for r in set.keys() {
|
||||
let v = match self.sessions.get(r) {
|
||||
Some(v) => v,
|
||||
@ -396,12 +401,7 @@ impl Handler<InnerMsg> for WsServer {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
for recipient in v.iter() {
|
||||
match recipient.do_send(InnerMsg::Transfer(msg.clone())) {
|
||||
Ok(_) => debug!("msg sent"),
|
||||
Err(e) => error!("{}", e),
|
||||
};
|
||||
}
|
||||
self.send_to_recipients(v, &msg);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
@ -413,6 +413,15 @@ impl WsServer {
|
||||
pub fn ensure_room(&mut self, room: i32) {
|
||||
self.rooms.entry(room).or_insert_with(HashMap::new);
|
||||
}
|
||||
|
||||
fn send_to_recipients(&self, recipients: &Vec<Recipient<InnerMsg>>, msg: &WsMsg) {
|
||||
for recipient in recipients.iter() {
|
||||
match recipient.do_send(InnerMsg::Transfer(msg.clone())) {
|
||||
Ok(_) => debug!("msg sent"),
|
||||
Err(e) => error!("{}", e),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/ws/")]
|
||||
|
Loading…
Reference in New Issue
Block a user