mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-24 17:28:40 +00:00
Persist chat mentions
This commit is contained in:
parent
821419ee5b
commit
d05404a4df
22 changed files with 402 additions and 112 deletions
|
@ -7,7 +7,10 @@ use gpui::{AppContext, ModelHandle};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use channel_buffer::{ChannelBuffer, ChannelBufferEvent, ACKNOWLEDGE_DEBOUNCE_INTERVAL};
|
pub use channel_buffer::{ChannelBuffer, ChannelBufferEvent, ACKNOWLEDGE_DEBOUNCE_INTERVAL};
|
||||||
pub use channel_chat::{ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId};
|
pub use channel_chat::{
|
||||||
|
mentions_to_proto, ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId,
|
||||||
|
MessageParams,
|
||||||
|
};
|
||||||
pub use channel_store::{
|
pub use channel_store::{
|
||||||
Channel, ChannelData, ChannelEvent, ChannelId, ChannelMembership, ChannelPath, ChannelStore,
|
Channel, ChannelData, ChannelEvent, ChannelId, ChannelMembership, ChannelPath, ChannelStore,
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@ use anyhow::{anyhow, Result};
|
||||||
use client::{
|
use client::{
|
||||||
proto,
|
proto,
|
||||||
user::{User, UserStore},
|
user::{User, UserStore},
|
||||||
Client, Subscription, TypedEnvelope,
|
Client, Subscription, TypedEnvelope, UserId,
|
||||||
};
|
};
|
||||||
use futures::lock::Mutex;
|
use futures::lock::Mutex;
|
||||||
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task};
|
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task};
|
||||||
|
@ -27,6 +27,12 @@ pub struct ChannelChat {
|
||||||
_subscription: Subscription,
|
_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct MessageParams {
|
||||||
|
pub text: String,
|
||||||
|
pub mentions: Vec<(Range<usize>, UserId)>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ChannelMessage {
|
pub struct ChannelMessage {
|
||||||
pub id: ChannelMessageId,
|
pub id: ChannelMessageId,
|
||||||
|
@ -34,6 +40,7 @@ pub struct ChannelMessage {
|
||||||
pub timestamp: OffsetDateTime,
|
pub timestamp: OffsetDateTime,
|
||||||
pub sender: Arc<User>,
|
pub sender: Arc<User>,
|
||||||
pub nonce: u128,
|
pub nonce: u128,
|
||||||
|
pub mentions: Vec<(Range<usize>, UserId)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -120,12 +127,16 @@ impl ChannelChat {
|
||||||
&self.channel
|
&self.channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn client(&self) -> &Arc<Client> {
|
||||||
|
&self.rpc
|
||||||
|
}
|
||||||
|
|
||||||
pub fn send_message(
|
pub fn send_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
body: String,
|
message: MessageParams,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Result<Task<Result<()>>> {
|
) -> Result<Task<Result<()>>> {
|
||||||
if body.is_empty() {
|
if message.text.is_empty() {
|
||||||
Err(anyhow!("message body can't be empty"))?;
|
Err(anyhow!("message body can't be empty"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,9 +153,10 @@ impl ChannelChat {
|
||||||
SumTree::from_item(
|
SumTree::from_item(
|
||||||
ChannelMessage {
|
ChannelMessage {
|
||||||
id: pending_id,
|
id: pending_id,
|
||||||
body: body.clone(),
|
body: message.text.clone(),
|
||||||
sender: current_user,
|
sender: current_user,
|
||||||
timestamp: OffsetDateTime::now_utc(),
|
timestamp: OffsetDateTime::now_utc(),
|
||||||
|
mentions: message.mentions.clone(),
|
||||||
nonce,
|
nonce,
|
||||||
},
|
},
|
||||||
&(),
|
&(),
|
||||||
|
@ -158,8 +170,9 @@ impl ChannelChat {
|
||||||
let outgoing_message_guard = outgoing_messages_lock.lock().await;
|
let outgoing_message_guard = outgoing_messages_lock.lock().await;
|
||||||
let request = rpc.request(proto::SendChannelMessage {
|
let request = rpc.request(proto::SendChannelMessage {
|
||||||
channel_id,
|
channel_id,
|
||||||
body,
|
body: message.text,
|
||||||
nonce: Some(nonce.into()),
|
nonce: Some(nonce.into()),
|
||||||
|
mentions: mentions_to_proto(&message.mentions),
|
||||||
});
|
});
|
||||||
let response = request.await?;
|
let response = request.await?;
|
||||||
drop(outgoing_message_guard);
|
drop(outgoing_message_guard);
|
||||||
|
@ -284,6 +297,7 @@ impl ChannelChat {
|
||||||
let request = rpc.request(proto::SendChannelMessage {
|
let request = rpc.request(proto::SendChannelMessage {
|
||||||
channel_id,
|
channel_id,
|
||||||
body: pending_message.body,
|
body: pending_message.body,
|
||||||
|
mentions: mentions_to_proto(&pending_message.mentions),
|
||||||
nonce: Some(pending_message.nonce.into()),
|
nonce: Some(pending_message.nonce.into()),
|
||||||
});
|
});
|
||||||
let response = request.await?;
|
let response = request.await?;
|
||||||
|
@ -471,6 +485,14 @@ impl ChannelMessage {
|
||||||
Ok(ChannelMessage {
|
Ok(ChannelMessage {
|
||||||
id: ChannelMessageId::Saved(message.id),
|
id: ChannelMessageId::Saved(message.id),
|
||||||
body: message.body,
|
body: message.body,
|
||||||
|
mentions: message
|
||||||
|
.mentions
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|mention| {
|
||||||
|
let range = mention.range?;
|
||||||
|
Some((range.start as usize..range.end as usize, mention.user_id))
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
timestamp: OffsetDateTime::from_unix_timestamp(message.timestamp as i64)?,
|
timestamp: OffsetDateTime::from_unix_timestamp(message.timestamp as i64)?,
|
||||||
sender,
|
sender,
|
||||||
nonce: message
|
nonce: message
|
||||||
|
@ -509,6 +531,19 @@ impl ChannelMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mentions_to_proto(mentions: &[(Range<usize>, UserId)]) -> Vec<proto::ChatMention> {
|
||||||
|
mentions
|
||||||
|
.iter()
|
||||||
|
.map(|(range, user_id)| proto::ChatMention {
|
||||||
|
range: Some(proto::Range {
|
||||||
|
start: range.start as u64,
|
||||||
|
end: range.end as u64,
|
||||||
|
}),
|
||||||
|
user_id: *user_id as u64,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
impl sum_tree::Item for ChannelMessage {
|
impl sum_tree::Item for ChannelMessage {
|
||||||
type Summary = ChannelMessageSummary;
|
type Summary = ChannelMessageSummary;
|
||||||
|
|
||||||
|
@ -547,3 +582,12 @@ impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for Count {
|
||||||
self.0 += summary.count;
|
self.0 += summary.count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for MessageParams {
|
||||||
|
fn from(value: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
text: value.into(),
|
||||||
|
mentions: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -194,6 +194,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
|
||||||
body: "a".into(),
|
body: "a".into(),
|
||||||
timestamp: 1000,
|
timestamp: 1000,
|
||||||
sender_id: 5,
|
sender_id: 5,
|
||||||
|
mentions: vec![],
|
||||||
nonce: Some(1.into()),
|
nonce: Some(1.into()),
|
||||||
},
|
},
|
||||||
proto::ChannelMessage {
|
proto::ChannelMessage {
|
||||||
|
@ -201,6 +202,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
|
||||||
body: "b".into(),
|
body: "b".into(),
|
||||||
timestamp: 1001,
|
timestamp: 1001,
|
||||||
sender_id: 6,
|
sender_id: 6,
|
||||||
|
mentions: vec![],
|
||||||
nonce: Some(2.into()),
|
nonce: Some(2.into()),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -247,6 +249,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
|
||||||
body: "c".into(),
|
body: "c".into(),
|
||||||
timestamp: 1002,
|
timestamp: 1002,
|
||||||
sender_id: 7,
|
sender_id: 7,
|
||||||
|
mentions: vec![],
|
||||||
nonce: Some(3.into()),
|
nonce: Some(3.into()),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -300,6 +303,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
|
||||||
timestamp: 998,
|
timestamp: 998,
|
||||||
sender_id: 5,
|
sender_id: 5,
|
||||||
nonce: Some(4.into()),
|
nonce: Some(4.into()),
|
||||||
|
mentions: vec![],
|
||||||
},
|
},
|
||||||
proto::ChannelMessage {
|
proto::ChannelMessage {
|
||||||
id: 9,
|
id: 9,
|
||||||
|
@ -307,6 +311,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
|
||||||
timestamp: 999,
|
timestamp: 999,
|
||||||
sender_id: 6,
|
sender_id: 6,
|
||||||
nonce: Some(5.into()),
|
nonce: Some(5.into()),
|
||||||
|
mentions: vec![],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -215,6 +215,14 @@ CREATE TABLE IF NOT EXISTS "channel_messages" (
|
||||||
CREATE INDEX "index_channel_messages_on_channel_id" ON "channel_messages" ("channel_id");
|
CREATE INDEX "index_channel_messages_on_channel_id" ON "channel_messages" ("channel_id");
|
||||||
CREATE UNIQUE INDEX "index_channel_messages_on_nonce" ON "channel_messages" ("nonce");
|
CREATE UNIQUE INDEX "index_channel_messages_on_nonce" ON "channel_messages" ("nonce");
|
||||||
|
|
||||||
|
CREATE TABLE "channel_message_mentions" (
|
||||||
|
"message_id" INTEGER NOT NULL REFERENCES channel_messages (id) ON DELETE CASCADE,
|
||||||
|
"start_offset" INTEGER NOT NULL,
|
||||||
|
"end_offset" INTEGER NOT NULL,
|
||||||
|
"user_id" INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY(message_id, start_offset)
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE "channel_paths" (
|
CREATE TABLE "channel_paths" (
|
||||||
"id_path" TEXT NOT NULL PRIMARY KEY,
|
"id_path" TEXT NOT NULL PRIMARY KEY,
|
||||||
"channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE
|
"channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE TABLE "channel_message_mentions" (
|
||||||
|
"message_id" INTEGER NOT NULL REFERENCES channel_messages (id) ON DELETE CASCADE,
|
||||||
|
"start_offset" INTEGER NOT NULL,
|
||||||
|
"end_offset" INTEGER NOT NULL,
|
||||||
|
"user_id" INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY(message_id, start_offset)
|
||||||
|
);
|
|
@ -418,7 +418,6 @@ pub struct WaitlistSummary {
|
||||||
pub struct NewUserParams {
|
pub struct NewUserParams {
|
||||||
pub github_login: String,
|
pub github_login: String,
|
||||||
pub github_user_id: i32,
|
pub github_user_id: i32,
|
||||||
pub invite_count: i32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -103,6 +103,7 @@ impl Database {
|
||||||
sender_id: row.sender_id.to_proto(),
|
sender_id: row.sender_id.to_proto(),
|
||||||
body: row.body,
|
body: row.body,
|
||||||
timestamp: row.sent_at.assume_utc().unix_timestamp() as u64,
|
timestamp: row.sent_at.assume_utc().unix_timestamp() as u64,
|
||||||
|
mentions: vec![],
|
||||||
nonce: Some(proto::Nonce {
|
nonce: Some(proto::Nonce {
|
||||||
upper_half: nonce.0,
|
upper_half: nonce.0,
|
||||||
lower_half: nonce.1,
|
lower_half: nonce.1,
|
||||||
|
@ -111,6 +112,38 @@ impl Database {
|
||||||
}
|
}
|
||||||
drop(rows);
|
drop(rows);
|
||||||
messages.reverse();
|
messages.reverse();
|
||||||
|
|
||||||
|
let mut mentions = channel_message_mention::Entity::find()
|
||||||
|
.filter(
|
||||||
|
channel_message_mention::Column::MessageId.is_in(messages.iter().map(|m| m.id)),
|
||||||
|
)
|
||||||
|
.order_by_asc(channel_message_mention::Column::MessageId)
|
||||||
|
.order_by_asc(channel_message_mention::Column::StartOffset)
|
||||||
|
.stream(&*tx)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut message_ix = 0;
|
||||||
|
while let Some(mention) = mentions.next().await {
|
||||||
|
let mention = mention?;
|
||||||
|
let message_id = mention.message_id.to_proto();
|
||||||
|
while let Some(message) = messages.get_mut(message_ix) {
|
||||||
|
if message.id < message_id {
|
||||||
|
message_ix += 1;
|
||||||
|
} else {
|
||||||
|
if message.id == message_id {
|
||||||
|
message.mentions.push(proto::ChatMention {
|
||||||
|
range: Some(proto::Range {
|
||||||
|
start: mention.start_offset as u64,
|
||||||
|
end: mention.end_offset as u64,
|
||||||
|
}),
|
||||||
|
user_id: mention.user_id.to_proto(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(messages)
|
Ok(messages)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
@ -121,6 +154,7 @@ impl Database {
|
||||||
channel_id: ChannelId,
|
channel_id: ChannelId,
|
||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
body: &str,
|
body: &str,
|
||||||
|
mentions: &[proto::ChatMention],
|
||||||
timestamp: OffsetDateTime,
|
timestamp: OffsetDateTime,
|
||||||
nonce: u128,
|
nonce: u128,
|
||||||
) -> Result<(MessageId, Vec<ConnectionId>, Vec<UserId>)> {
|
) -> Result<(MessageId, Vec<ConnectionId>, Vec<UserId>)> {
|
||||||
|
@ -150,7 +184,7 @@ impl Database {
|
||||||
let timestamp = timestamp.to_offset(time::UtcOffset::UTC);
|
let timestamp = timestamp.to_offset(time::UtcOffset::UTC);
|
||||||
let timestamp = time::PrimitiveDateTime::new(timestamp.date(), timestamp.time());
|
let timestamp = time::PrimitiveDateTime::new(timestamp.date(), timestamp.time());
|
||||||
|
|
||||||
let message = channel_message::Entity::insert(channel_message::ActiveModel {
|
let message_id = channel_message::Entity::insert(channel_message::ActiveModel {
|
||||||
channel_id: ActiveValue::Set(channel_id),
|
channel_id: ActiveValue::Set(channel_id),
|
||||||
sender_id: ActiveValue::Set(user_id),
|
sender_id: ActiveValue::Set(user_id),
|
||||||
body: ActiveValue::Set(body.to_string()),
|
body: ActiveValue::Set(body.to_string()),
|
||||||
|
@ -164,7 +198,31 @@ impl Database {
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
)
|
)
|
||||||
.exec(&*tx)
|
.exec(&*tx)
|
||||||
.await?;
|
.await?
|
||||||
|
.last_insert_id;
|
||||||
|
|
||||||
|
let models = mentions
|
||||||
|
.iter()
|
||||||
|
.filter_map(|mention| {
|
||||||
|
let range = mention.range.as_ref()?;
|
||||||
|
if !body.is_char_boundary(range.start as usize)
|
||||||
|
|| !body.is_char_boundary(range.end as usize)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(channel_message_mention::ActiveModel {
|
||||||
|
message_id: ActiveValue::Set(message_id),
|
||||||
|
start_offset: ActiveValue::Set(range.start as i32),
|
||||||
|
end_offset: ActiveValue::Set(range.end as i32),
|
||||||
|
user_id: ActiveValue::Set(UserId::from_proto(mention.user_id)),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if !models.is_empty() {
|
||||||
|
channel_message_mention::Entity::insert_many(models)
|
||||||
|
.exec(&*tx)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, EnumIter, DeriveColumn)]
|
#[derive(Debug, Clone, Copy, EnumIter, DeriveColumn)]
|
||||||
enum QueryConnectionId {
|
enum QueryConnectionId {
|
||||||
|
@ -172,22 +230,13 @@ impl Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observe this message for the sender
|
// Observe this message for the sender
|
||||||
self.observe_channel_message_internal(
|
self.observe_channel_message_internal(channel_id, user_id, message_id, &*tx)
|
||||||
channel_id,
|
.await?;
|
||||||
user_id,
|
|
||||||
message.last_insert_id,
|
|
||||||
&*tx,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut channel_members = self.get_channel_members_internal(channel_id, &*tx).await?;
|
let mut channel_members = self.get_channel_members_internal(channel_id, &*tx).await?;
|
||||||
channel_members.retain(|member| !participant_user_ids.contains(member));
|
channel_members.retain(|member| !participant_user_ids.contains(member));
|
||||||
|
|
||||||
Ok((
|
Ok((message_id, participant_connection_ids, channel_members))
|
||||||
message.last_insert_id,
|
|
||||||
participant_connection_ids,
|
|
||||||
channel_members,
|
|
||||||
))
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ pub mod channel_buffer_collaborator;
|
||||||
pub mod channel_chat_participant;
|
pub mod channel_chat_participant;
|
||||||
pub mod channel_member;
|
pub mod channel_member;
|
||||||
pub mod channel_message;
|
pub mod channel_message;
|
||||||
|
pub mod channel_message_mention;
|
||||||
pub mod channel_path;
|
pub mod channel_path;
|
||||||
pub mod contact;
|
pub mod contact;
|
||||||
pub mod feature_flag;
|
pub mod feature_flag;
|
||||||
|
|
43
crates/collab/src/db/tables/channel_message_mention.rs
Normal file
43
crates/collab/src/db/tables/channel_message_mention.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use crate::db::{MessageId, UserId};
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "channel_message_mentions")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub message_id: MessageId,
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub start_offset: i32,
|
||||||
|
pub end_offset: i32,
|
||||||
|
pub user_id: UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::channel_message::Entity",
|
||||||
|
from = "Column::MessageId",
|
||||||
|
to = "super::channel_message::Column::Id"
|
||||||
|
)]
|
||||||
|
Message,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::user::Entity",
|
||||||
|
from = "Column::UserId",
|
||||||
|
to = "super::user::Column::Id"
|
||||||
|
)]
|
||||||
|
MentionedUser,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::channel::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Message.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::user::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::MentionedUser.def()
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,6 @@ async fn test_channel_buffers(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user_a".into(),
|
github_login: "user_a".into(),
|
||||||
github_user_id: 101,
|
github_user_id: 101,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -30,7 +29,6 @@ async fn test_channel_buffers(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user_b".into(),
|
github_login: "user_b".into(),
|
||||||
github_user_id: 102,
|
github_user_id: 102,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -45,7 +43,6 @@ async fn test_channel_buffers(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user_c".into(),
|
github_login: "user_c".into(),
|
||||||
github_user_id: 102,
|
github_user_id: 102,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -178,7 +175,6 @@ async fn test_channel_buffers_last_operations(db: &Database) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user_a".into(),
|
github_login: "user_a".into(),
|
||||||
github_user_id: 101,
|
github_user_id: 101,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -191,7 +187,6 @@ async fn test_channel_buffers_last_operations(db: &Database) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user_b".into(),
|
github_login: "user_b".into(),
|
||||||
github_user_id: 102,
|
github_user_id: 102,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -24,7 +24,6 @@ async fn test_channels(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user1".into(),
|
github_login: "user1".into(),
|
||||||
github_user_id: 5,
|
github_user_id: 5,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -38,7 +37,6 @@ async fn test_channels(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user2".into(),
|
github_login: "user2".into(),
|
||||||
github_user_id: 6,
|
github_user_id: 6,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -179,7 +177,6 @@ async fn test_joining_channels(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user1".into(),
|
github_login: "user1".into(),
|
||||||
github_user_id: 5,
|
github_user_id: 5,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -192,7 +189,6 @@ async fn test_joining_channels(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user2".into(),
|
github_login: "user2".into(),
|
||||||
github_user_id: 6,
|
github_user_id: 6,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -246,7 +242,6 @@ async fn test_channel_invites(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user1".into(),
|
github_login: "user1".into(),
|
||||||
github_user_id: 5,
|
github_user_id: 5,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -259,7 +254,6 @@ async fn test_channel_invites(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user2".into(),
|
github_login: "user2".into(),
|
||||||
github_user_id: 6,
|
github_user_id: 6,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -273,7 +267,6 @@ async fn test_channel_invites(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user3".into(),
|
github_login: "user3".into(),
|
||||||
github_user_id: 7,
|
github_user_id: 7,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -385,7 +378,6 @@ async fn test_channel_renames(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user1".into(),
|
github_login: "user1".into(),
|
||||||
github_user_id: 5,
|
github_user_id: 5,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -399,7 +391,6 @@ async fn test_channel_renames(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user2".into(),
|
github_login: "user2".into(),
|
||||||
github_user_id: 6,
|
github_user_id: 6,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -444,7 +435,6 @@ async fn test_db_channel_moving(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user1".into(),
|
github_login: "user1".into(),
|
||||||
github_user_id: 5,
|
github_user_id: 5,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -802,7 +792,6 @@ async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user1".into(),
|
github_login: "user1".into(),
|
||||||
github_user_id: 5,
|
github_user_id: 5,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -22,7 +22,6 @@ async fn test_get_users(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: format!("user{i}"),
|
github_login: format!("user{i}"),
|
||||||
github_user_id: i,
|
github_user_id: i,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -88,7 +87,6 @@ async fn test_get_or_create_user_by_github_account(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "login1".into(),
|
github_login: "login1".into(),
|
||||||
github_user_id: 101,
|
github_user_id: 101,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -101,7 +99,6 @@ async fn test_get_or_create_user_by_github_account(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "login2".into(),
|
github_login: "login2".into(),
|
||||||
github_user_id: 102,
|
github_user_id: 102,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -156,7 +153,6 @@ async fn test_create_access_tokens(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "u1".into(),
|
github_login: "u1".into(),
|
||||||
github_user_id: 1,
|
github_user_id: 1,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -238,7 +234,6 @@ async fn test_add_contacts(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: format!("user{i}"),
|
github_login: format!("user{i}"),
|
||||||
github_user_id: i,
|
github_user_id: i,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -400,7 +395,6 @@ async fn test_metrics_id(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "person1".into(),
|
github_login: "person1".into(),
|
||||||
github_user_id: 101,
|
github_user_id: 101,
|
||||||
invite_count: 5,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -416,7 +410,6 @@ async fn test_metrics_id(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "person2".into(),
|
github_login: "person2".into(),
|
||||||
github_user_id: 102,
|
github_user_id: 102,
|
||||||
invite_count: 5,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -445,7 +438,6 @@ async fn test_project_count(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "admin".into(),
|
github_login: "admin".into(),
|
||||||
github_user_id: 0,
|
github_user_id: 0,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -457,7 +449,6 @@ async fn test_project_count(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user".into(),
|
github_login: "user".into(),
|
||||||
github_user_id: 1,
|
github_user_id: 1,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -539,7 +530,6 @@ async fn test_fuzzy_search_users() {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: github_login.into(),
|
github_login: github_login.into(),
|
||||||
github_user_id: i as i32,
|
github_user_id: i as i32,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -581,7 +571,6 @@ async fn test_non_matching_release_channels(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "admin".into(),
|
github_login: "admin".into(),
|
||||||
github_user_id: 0,
|
github_user_id: 0,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -593,7 +582,6 @@ async fn test_non_matching_release_channels(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user".into(),
|
github_login: "user".into(),
|
||||||
github_user_id: 1,
|
github_user_id: 1,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -18,7 +18,6 @@ async fn test_get_user_flags(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: format!("user1"),
|
github_login: format!("user1"),
|
||||||
github_user_id: 1,
|
github_user_id: 1,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -32,7 +31,6 @@ async fn test_get_user_flags(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: format!("user2"),
|
github_login: format!("user2"),
|
||||||
github_user_id: 2,
|
github_user_id: 2,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::{
|
||||||
db::{Database, MessageId, NewUserParams},
|
db::{Database, MessageId, NewUserParams},
|
||||||
test_both_dbs,
|
test_both_dbs,
|
||||||
};
|
};
|
||||||
|
use channel::mentions_to_proto;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
@ -19,7 +20,6 @@ async fn test_channel_message_retrieval(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user".into(),
|
github_login: "user".into(),
|
||||||
github_user_id: 1,
|
github_user_id: 1,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -35,11 +35,18 @@ async fn test_channel_message_retrieval(db: &Arc<Database>) {
|
||||||
let mut all_messages = Vec::new();
|
let mut all_messages = Vec::new();
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
all_messages.push(
|
all_messages.push(
|
||||||
db.create_channel_message(channel, user, &i.to_string(), OffsetDateTime::now_utc(), i)
|
db.create_channel_message(
|
||||||
.await
|
channel,
|
||||||
.unwrap()
|
user,
|
||||||
.0
|
&i.to_string(),
|
||||||
.to_proto(),
|
&[],
|
||||||
|
OffsetDateTime::now_utc(),
|
||||||
|
i,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.0
|
||||||
|
.to_proto(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +88,6 @@ async fn test_channel_message_nonces(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user".into(),
|
github_login: "user".into(),
|
||||||
github_user_id: 1,
|
github_user_id: 1,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -96,19 +102,19 @@ async fn test_channel_message_nonces(db: &Arc<Database>) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let msg1_id = db
|
let msg1_id = db
|
||||||
.create_channel_message(channel, user, "1", OffsetDateTime::now_utc(), 1)
|
.create_channel_message(channel, user, "1", &[], OffsetDateTime::now_utc(), 1)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let msg2_id = db
|
let msg2_id = db
|
||||||
.create_channel_message(channel, user, "2", OffsetDateTime::now_utc(), 2)
|
.create_channel_message(channel, user, "2", &[], OffsetDateTime::now_utc(), 2)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let msg3_id = db
|
let msg3_id = db
|
||||||
.create_channel_message(channel, user, "3", OffsetDateTime::now_utc(), 1)
|
.create_channel_message(channel, user, "3", &[], OffsetDateTime::now_utc(), 1)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let msg4_id = db
|
let msg4_id = db
|
||||||
.create_channel_message(channel, user, "4", OffsetDateTime::now_utc(), 2)
|
.create_channel_message(channel, user, "4", &[], OffsetDateTime::now_utc(), 2)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -118,12 +124,12 @@ async fn test_channel_message_nonces(db: &Arc<Database>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
test_both_dbs!(
|
test_both_dbs!(
|
||||||
test_channel_message_new_notification,
|
test_unseen_channel_messages,
|
||||||
test_channel_message_new_notification_postgres,
|
test_unseen_channel_messages_postgres,
|
||||||
test_channel_message_new_notification_sqlite
|
test_unseen_channel_messages_sqlite
|
||||||
);
|
);
|
||||||
|
|
||||||
async fn test_channel_message_new_notification(db: &Arc<Database>) {
|
async fn test_unseen_channel_messages(db: &Arc<Database>) {
|
||||||
let user = db
|
let user = db
|
||||||
.create_user(
|
.create_user(
|
||||||
"user_a@example.com",
|
"user_a@example.com",
|
||||||
|
@ -131,7 +137,6 @@ async fn test_channel_message_new_notification(db: &Arc<Database>) {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user_a".into(),
|
github_login: "user_a".into(),
|
||||||
github_user_id: 1,
|
github_user_id: 1,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -143,8 +148,7 @@ async fn test_channel_message_new_notification(db: &Arc<Database>) {
|
||||||
false,
|
false,
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: "user_b".into(),
|
github_login: "user_b".into(),
|
||||||
github_user_id: 1,
|
github_user_id: 2,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -152,21 +156,17 @@ async fn test_channel_message_new_notification(db: &Arc<Database>) {
|
||||||
.user_id;
|
.user_id;
|
||||||
|
|
||||||
let channel_1 = db.create_channel("channel", None, user).await.unwrap();
|
let channel_1 = db.create_channel("channel", None, user).await.unwrap();
|
||||||
|
|
||||||
let channel_2 = db.create_channel("channel-2", None, user).await.unwrap();
|
let channel_2 = db.create_channel("channel-2", None, user).await.unwrap();
|
||||||
|
|
||||||
db.invite_channel_member(channel_1, observer, user, false)
|
db.invite_channel_member(channel_1, observer, user, false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
db.respond_to_channel_invite(channel_1, observer, true)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.invite_channel_member(channel_2, observer, user, false)
|
db.invite_channel_member(channel_2, observer, user, false)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
db.respond_to_channel_invite(channel_1, observer, true)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
db.respond_to_channel_invite(channel_2, observer, true)
|
db.respond_to_channel_invite(channel_2, observer, true)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -179,17 +179,17 @@ async fn test_channel_message_new_notification(db: &Arc<Database>) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let _ = db
|
let _ = db
|
||||||
.create_channel_message(channel_1, user, "1_1", OffsetDateTime::now_utc(), 1)
|
.create_channel_message(channel_1, user, "1_1", &[], OffsetDateTime::now_utc(), 1)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (second_message, _, _) = db
|
let (second_message, _, _) = db
|
||||||
.create_channel_message(channel_1, user, "1_2", OffsetDateTime::now_utc(), 2)
|
.create_channel_message(channel_1, user, "1_2", &[], OffsetDateTime::now_utc(), 2)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (third_message, _, _) = db
|
let (third_message, _, _) = db
|
||||||
.create_channel_message(channel_1, user, "1_3", OffsetDateTime::now_utc(), 3)
|
.create_channel_message(channel_1, user, "1_3", &[], OffsetDateTime::now_utc(), 3)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ async fn test_channel_message_new_notification(db: &Arc<Database>) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (fourth_message, _, _) = db
|
let (fourth_message, _, _) = db
|
||||||
.create_channel_message(channel_2, user, "2_1", OffsetDateTime::now_utc(), 4)
|
.create_channel_message(channel_2, user, "2_1", &[], OffsetDateTime::now_utc(), 4)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -295,3 +295,129 @@ async fn test_channel_message_new_notification(db: &Arc<Database>) {
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_both_dbs!(
|
||||||
|
test_channel_message_mentions,
|
||||||
|
test_channel_message_mentions_postgres,
|
||||||
|
test_channel_message_mentions_sqlite
|
||||||
|
);
|
||||||
|
|
||||||
|
async fn test_channel_message_mentions(db: &Arc<Database>) {
|
||||||
|
let user_a = db
|
||||||
|
.create_user(
|
||||||
|
"user_a@example.com",
|
||||||
|
false,
|
||||||
|
NewUserParams {
|
||||||
|
github_login: "user_a".into(),
|
||||||
|
github_user_id: 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.user_id;
|
||||||
|
let user_b = db
|
||||||
|
.create_user(
|
||||||
|
"user_b@example.com",
|
||||||
|
false,
|
||||||
|
NewUserParams {
|
||||||
|
github_login: "user_b".into(),
|
||||||
|
github_user_id: 2,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.user_id;
|
||||||
|
let user_c = db
|
||||||
|
.create_user(
|
||||||
|
"user_b@example.com",
|
||||||
|
false,
|
||||||
|
NewUserParams {
|
||||||
|
github_login: "user_c".into(),
|
||||||
|
github_user_id: 3,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.user_id;
|
||||||
|
|
||||||
|
let channel = db.create_channel("channel", None, user_a).await.unwrap();
|
||||||
|
db.invite_channel_member(channel, user_b, user_a, false)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
db.respond_to_channel_invite(channel, user_b, true)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let owner_id = db.create_server("test").await.unwrap().0 as u32;
|
||||||
|
let connection_id = rpc::ConnectionId { owner_id, id: 0 };
|
||||||
|
db.join_channel_chat(channel, connection_id, user_a)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
db.create_channel_message(
|
||||||
|
channel,
|
||||||
|
user_a,
|
||||||
|
"hi @user_b and @user_c",
|
||||||
|
&mentions_to_proto(&[(3..10, user_b.to_proto()), (15..22, user_c.to_proto())]),
|
||||||
|
OffsetDateTime::now_utc(),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
db.create_channel_message(
|
||||||
|
channel,
|
||||||
|
user_a,
|
||||||
|
"bye @user_c",
|
||||||
|
&mentions_to_proto(&[(4..11, user_c.to_proto())]),
|
||||||
|
OffsetDateTime::now_utc(),
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
db.create_channel_message(
|
||||||
|
channel,
|
||||||
|
user_a,
|
||||||
|
"umm",
|
||||||
|
&mentions_to_proto(&[]),
|
||||||
|
OffsetDateTime::now_utc(),
|
||||||
|
3,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
db.create_channel_message(
|
||||||
|
channel,
|
||||||
|
user_a,
|
||||||
|
"@user_b, stop.",
|
||||||
|
&mentions_to_proto(&[(0..7, user_b.to_proto())]),
|
||||||
|
OffsetDateTime::now_utc(),
|
||||||
|
4,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let messages = db
|
||||||
|
.get_channel_messages(channel, user_b, 5, None)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| (m.body, m.mentions))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert_eq!(
|
||||||
|
&messages,
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"hi @user_b and @user_c".into(),
|
||||||
|
mentions_to_proto(&[(3..10, user_b.to_proto()), (15..22, user_c.to_proto())]),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"bye @user_c".into(),
|
||||||
|
mentions_to_proto(&[(4..11, user_c.to_proto())]),
|
||||||
|
),
|
||||||
|
("umm".into(), mentions_to_proto(&[]),),
|
||||||
|
(
|
||||||
|
"@user_b, stop.".into(),
|
||||||
|
mentions_to_proto(&[(0..7, user_b.to_proto())]),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -2885,6 +2885,8 @@ async fn send_channel_message(
|
||||||
return Err(anyhow!("message can't be blank"))?;
|
return Err(anyhow!("message can't be blank"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: adjust mentions if body is trimmed
|
||||||
|
|
||||||
let timestamp = OffsetDateTime::now_utc();
|
let timestamp = OffsetDateTime::now_utc();
|
||||||
let nonce = request
|
let nonce = request
|
||||||
.nonce
|
.nonce
|
||||||
|
@ -2898,6 +2900,7 @@ async fn send_channel_message(
|
||||||
channel_id,
|
channel_id,
|
||||||
session.user_id,
|
session.user_id,
|
||||||
&body,
|
&body,
|
||||||
|
&request.mentions,
|
||||||
timestamp,
|
timestamp,
|
||||||
nonce.clone().into(),
|
nonce.clone().into(),
|
||||||
)
|
)
|
||||||
|
@ -2906,6 +2909,7 @@ async fn send_channel_message(
|
||||||
sender_id: session.user_id.to_proto(),
|
sender_id: session.user_id.to_proto(),
|
||||||
id: message_id.to_proto(),
|
id: message_id.to_proto(),
|
||||||
body,
|
body,
|
||||||
|
mentions: request.mentions,
|
||||||
timestamp: timestamp.unix_timestamp() as u64,
|
timestamp: timestamp.unix_timestamp() as u64,
|
||||||
nonce: Some(nonce),
|
nonce: Some(nonce),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
|
use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
|
||||||
use channel::{ChannelChat, ChannelMessageId};
|
use channel::{ChannelChat, ChannelMessageId, MessageParams};
|
||||||
use collab_ui::chat_panel::ChatPanel;
|
use collab_ui::chat_panel::ChatPanel;
|
||||||
use gpui::{executor::Deterministic, BorrowAppContext, ModelHandle, TestAppContext};
|
use gpui::{executor::Deterministic, BorrowAppContext, ModelHandle, TestAppContext};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -8,20 +8,22 @@ use workspace::dock::Panel;
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_basic_channel_messages(
|
async fn test_basic_channel_messages(
|
||||||
deterministic: Arc<Deterministic>,
|
deterministic: Arc<Deterministic>,
|
||||||
cx_a: &mut TestAppContext,
|
mut cx_a: &mut TestAppContext,
|
||||||
cx_b: &mut TestAppContext,
|
mut cx_b: &mut TestAppContext,
|
||||||
|
mut cx_c: &mut TestAppContext,
|
||||||
) {
|
) {
|
||||||
deterministic.forbid_parking();
|
deterministic.forbid_parking();
|
||||||
let mut server = TestServer::start(&deterministic).await;
|
let mut server = TestServer::start(&deterministic).await;
|
||||||
let client_a = server.create_client(cx_a, "user_a").await;
|
let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
let client_b = server.create_client(cx_b, "user_b").await;
|
let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
|
let client_c = server.create_client(cx_c, "user_c").await;
|
||||||
|
|
||||||
let channel_id = server
|
let channel_id = server
|
||||||
.make_channel(
|
.make_channel(
|
||||||
"the-channel",
|
"the-channel",
|
||||||
None,
|
None,
|
||||||
(&client_a, cx_a),
|
(&client_a, cx_a),
|
||||||
&mut [(&client_b, cx_b)],
|
&mut [(&client_b, cx_b), (&client_c, cx_c)],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -37,7 +39,16 @@ async fn test_basic_channel_messages(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
channel_chat_a
|
channel_chat_a
|
||||||
.update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
|
.update(cx_a, |c, cx| {
|
||||||
|
c.send_message(
|
||||||
|
MessageParams {
|
||||||
|
text: "hi @user_c!".into(),
|
||||||
|
mentions: vec![(3..10, client_c.id())],
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
channel_chat_a
|
channel_chat_a
|
||||||
|
@ -52,15 +63,34 @@ async fn test_basic_channel_messages(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
deterministic.run_until_parked();
|
deterministic.run_until_parked();
|
||||||
channel_chat_a.update(cx_a, |c, _| {
|
|
||||||
assert_eq!(
|
let channel_chat_c = client_c
|
||||||
c.messages()
|
.channel_store()
|
||||||
.iter()
|
.update(cx_c, |store, cx| store.open_channel_chat(channel_id, cx))
|
||||||
.map(|m| m.body.as_str())
|
.await
|
||||||
.collect::<Vec<_>>(),
|
.unwrap();
|
||||||
vec!["one", "two", "three"]
|
|
||||||
);
|
for (chat, cx) in [
|
||||||
})
|
(&channel_chat_a, &mut cx_a),
|
||||||
|
(&channel_chat_b, &mut cx_b),
|
||||||
|
(&channel_chat_c, &mut cx_c),
|
||||||
|
] {
|
||||||
|
chat.update(*cx, |c, _| {
|
||||||
|
assert_eq!(
|
||||||
|
c.messages()
|
||||||
|
.iter()
|
||||||
|
.map(|m| (m.body.as_str(), m.mentions.as_slice()))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
vec![
|
||||||
|
("hi @user_c!", [(3..10, client_c.id())].as_slice()),
|
||||||
|
("two", &[]),
|
||||||
|
("three", &[])
|
||||||
|
],
|
||||||
|
"results for user {}",
|
||||||
|
c.client().id(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
|
|
@ -208,8 +208,7 @@ impl<T: RandomizedTest> TestPlan<T> {
|
||||||
false,
|
false,
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: username.clone(),
|
github_login: username.clone(),
|
||||||
github_user_id: (ix + 1) as i32,
|
github_user_id: ix as i32,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -140,7 +140,6 @@ impl TestServer {
|
||||||
NewUserParams {
|
NewUserParams {
|
||||||
github_login: name.into(),
|
github_login: name.into(),
|
||||||
github_user_id: 0,
|
github_user_id: 0,
|
||||||
invite_count: 0,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -599,7 +599,7 @@ impl ChatPanel {
|
||||||
.update(cx, |editor, cx| editor.take_message(cx));
|
.update(cx, |editor, cx| editor.take_message(cx));
|
||||||
|
|
||||||
if let Some(task) = chat
|
if let Some(task) = chat
|
||||||
.update(cx, |chat, cx| chat.send_message(message.text, cx))
|
.update(cx, |chat, cx| chat.send_message(message, cx))
|
||||||
.log_err()
|
.log_err()
|
||||||
{
|
{
|
||||||
task.detach();
|
task.detach();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use channel::{Channel, ChannelMembership, ChannelStore};
|
use channel::{Channel, ChannelMembership, ChannelStore, MessageParams};
|
||||||
use client::UserId;
|
use client::UserId;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use editor::{AnchorRangeExt, Editor};
|
use editor::{AnchorRangeExt, Editor};
|
||||||
|
@ -9,7 +9,7 @@ use gpui::{
|
||||||
use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry};
|
use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use project::search::SearchQuery;
|
use project::search::SearchQuery;
|
||||||
use std::{ops::Range, sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
const MENTIONS_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(50);
|
const MENTIONS_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(50);
|
||||||
|
|
||||||
|
@ -33,12 +33,6 @@ pub struct MessageEditor {
|
||||||
channel: Option<Arc<Channel>>,
|
channel: Option<Arc<Channel>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub struct ChatMessage {
|
|
||||||
pub text: String,
|
|
||||||
pub mentions: Vec<(Range<usize>, UserId)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MessageEditor {
|
impl MessageEditor {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
|
@ -102,7 +96,7 @@ impl MessageEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_members(&mut self, members: Vec<ChannelMembership>, cx: &mut ViewContext<Self>) {
|
pub fn set_members(&mut self, members: Vec<ChannelMembership>, _: &mut ViewContext<Self>) {
|
||||||
self.users.clear();
|
self.users.clear();
|
||||||
self.users.extend(
|
self.users.extend(
|
||||||
members
|
members
|
||||||
|
@ -111,7 +105,7 @@ impl MessageEditor {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_message(&mut self, cx: &mut ViewContext<Self>) -> ChatMessage {
|
pub fn take_message(&mut self, cx: &mut ViewContext<Self>) -> MessageParams {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
let highlights = editor.text_highlights::<Self>(cx);
|
let highlights = editor.text_highlights::<Self>(cx);
|
||||||
let text = editor.text(cx);
|
let text = editor.text(cx);
|
||||||
|
@ -129,7 +123,7 @@ impl MessageEditor {
|
||||||
editor.clear(cx);
|
editor.clear(cx);
|
||||||
self.mentions.clear();
|
self.mentions.clear();
|
||||||
|
|
||||||
ChatMessage { text, mentions }
|
MessageParams { text, mentions }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +258,7 @@ mod tests {
|
||||||
let (text, ranges) = marked_text_ranges("Hello, «@a-b»! Have you met «@C_D»?", false);
|
let (text, ranges) = marked_text_ranges("Hello, «@a-b»! Have you met «@C_D»?", false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor.take_message(cx),
|
editor.take_message(cx),
|
||||||
ChatMessage {
|
MessageParams {
|
||||||
text,
|
text,
|
||||||
mentions: vec![(ranges[0].clone(), 101), (ranges[1].clone(), 102)],
|
mentions: vec![(ranges[0].clone(), 101), (ranges[1].clone(), 102)],
|
||||||
}
|
}
|
||||||
|
|
|
@ -3286,8 +3286,10 @@ impl Editor {
|
||||||
i = 0;
|
i = 0;
|
||||||
} else if pair_state.range.start.to_offset(buffer) > range.end {
|
} else if pair_state.range.start.to_offset(buffer) > range.end {
|
||||||
break;
|
break;
|
||||||
} else if pair_state.selection_id == selection.id {
|
} else {
|
||||||
enclosing = Some(pair_state);
|
if pair_state.selection_id == selection.id {
|
||||||
|
enclosing = Some(pair_state);
|
||||||
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1088,6 +1088,7 @@ message SendChannelMessage {
|
||||||
uint64 channel_id = 1;
|
uint64 channel_id = 1;
|
||||||
string body = 2;
|
string body = 2;
|
||||||
Nonce nonce = 3;
|
Nonce nonce = 3;
|
||||||
|
repeated ChatMention mentions = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RemoveChannelMessage {
|
message RemoveChannelMessage {
|
||||||
|
@ -1149,6 +1150,12 @@ message ChannelMessage {
|
||||||
uint64 timestamp = 3;
|
uint64 timestamp = 3;
|
||||||
uint64 sender_id = 4;
|
uint64 sender_id = 4;
|
||||||
Nonce nonce = 5;
|
Nonce nonce = 5;
|
||||||
|
repeated ChatMention mentions = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ChatMention {
|
||||||
|
Range range = 1;
|
||||||
|
uint64 user_id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RejoinChannelBuffers {
|
message RejoinChannelBuffers {
|
||||||
|
|
Loading…
Reference in a new issue