Add notifications2

This commit is contained in:
Mikayla 2023-11-08 21:23:31 -08:00
parent 8330fb5f10
commit 7888dc4592
No known key found for this signature in database
11 changed files with 786 additions and 267 deletions

View file

@ -1,115 +1,115 @@
use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
use channel::{ChannelChat, ChannelMessageId};
use channel::{ChannelChat, ChannelMessageId, MessageParams};
use gpui::{BackgroundExecutor, Model, TestAppContext};
use rpc::Notification;
// todo!(notifications)
// #[gpui::test]
// async fn test_basic_channel_messages(
// executor: BackgroundExecutor,
// mut cx_a: &mut TestAppContext,
// mut cx_b: &mut TestAppContext,
// mut cx_c: &mut TestAppContext,
// ) {
// let mut server = TestServer::start(executor.clone()).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_c = server.create_client(cx_c, "user_c").await;
#[gpui::test]
async fn test_basic_channel_messages(
executor: BackgroundExecutor,
mut cx_a: &mut TestAppContext,
mut cx_b: &mut TestAppContext,
mut cx_c: &mut TestAppContext,
) {
let mut server = TestServer::start(executor.clone()).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_c = server.create_client(cx_c, "user_c").await;
// let channel_id = server
// .make_channel(
// "the-channel",
// None,
// (&client_a, cx_a),
// &mut [(&client_b, cx_b), (&client_c, cx_c)],
// )
// .await;
let channel_id = server
.make_channel(
"the-channel",
None,
(&client_a, cx_a),
&mut [(&client_b, cx_b), (&client_c, cx_c)],
)
.await;
// let channel_chat_a = client_a
// .channel_store()
// .update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
// .await
// .unwrap();
// let channel_chat_b = client_b
// .channel_store()
// .update(cx_b, |store, cx| store.open_channel_chat(channel_id, cx))
// .await
// .unwrap();
let channel_chat_a = client_a
.channel_store()
.update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
.await
.unwrap();
let channel_chat_b = client_b
.channel_store()
.update(cx_b, |store, cx| store.open_channel_chat(channel_id, cx))
.await
.unwrap();
// let message_id = channel_chat_a
// .update(cx_a, |c, cx| {
// c.send_message(
// MessageParams {
// text: "hi @user_c!".into(),
// mentions: vec![(3..10, client_c.id())],
// },
// cx,
// )
// .unwrap()
// })
// .await
// .unwrap();
// channel_chat_a
// .update(cx_a, |c, cx| c.send_message("two".into(), cx).unwrap())
// .await
// .unwrap();
let message_id = channel_chat_a
.update(cx_a, |c, cx| {
c.send_message(
MessageParams {
text: "hi @user_c!".into(),
mentions: vec![(3..10, client_c.id())],
},
cx,
)
.unwrap()
})
.await
.unwrap();
channel_chat_a
.update(cx_a, |c, cx| c.send_message("two".into(), cx).unwrap())
.await
.unwrap();
// executor.run_until_parked();
// channel_chat_b
// .update(cx_b, |c, cx| c.send_message("three".into(), cx).unwrap())
// .await
// .unwrap();
executor.run_until_parked();
channel_chat_b
.update(cx_b, |c, cx| c.send_message("three".into(), cx).unwrap())
.await
.unwrap();
// executor.run_until_parked();
executor.run_until_parked();
// let channel_chat_c = client_c
// .channel_store()
// .update(cx_c, |store, cx| store.open_channel_chat(channel_id, cx))
// .await
// .unwrap();
let channel_chat_c = client_c
.channel_store()
.update(cx_c, |store, cx| store.open_channel_chat(channel_id, cx))
.await
.unwrap();
// 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(),
// );
// });
// }
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(),
);
});
}
// client_c.notification_store().update(cx_c, |store, _| {
// assert_eq!(store.notification_count(), 2);
// assert_eq!(store.unread_notification_count(), 1);
// assert_eq!(
// store.notification_at(0).unwrap().notification,
// Notification::ChannelMessageMention {
// message_id,
// sender_id: client_a.id(),
// channel_id,
// }
// );
// assert_eq!(
// store.notification_at(1).unwrap().notification,
// Notification::ChannelInvitation {
// channel_id,
// channel_name: "the-channel".to_string(),
// inviter_id: client_a.id()
// }
// );
// });
// }
client_c.notification_store().update(cx_c, |store, _| {
assert_eq!(store.notification_count(), 2);
assert_eq!(store.unread_notification_count(), 1);
assert_eq!(
store.notification_at(0).unwrap().notification,
Notification::ChannelMessageMention {
message_id,
sender_id: client_a.id(),
channel_id,
}
);
assert_eq!(
store.notification_at(1).unwrap().notification,
Notification::ChannelInvitation {
channel_id,
channel_name: "the-channel".to_string(),
inviter_id: client_a.id()
}
);
});
}
#[gpui::test]
async fn test_rejoin_channel_chat(

View file

@ -1128,6 +1128,8 @@ async fn test_channel_link_notifications(
.await
.unwrap();
executor.run_until_parked();
// the members-only channel is still shown for c, but hidden for b
assert_channels_list_shape(
client_b.channel_store(),

View file

@ -1,160 +1,160 @@
//todo!(notifications)
// use crate::tests::TestServer;
// use gpui::{executor::Deterministic, TestAppContext};
// use notifications::NotificationEvent;
// use parking_lot::Mutex;
// use rpc::{proto, Notification};
// use std::sync::Arc;
use std::sync::Arc;
// #[gpui::test]
// async fn test_notifications(
// deterministic: Arc<Deterministic>,
// cx_a: &mut TestAppContext,
// cx_b: &mut TestAppContext,
// ) {
// deterministic.forbid_parking();
// let mut server = TestServer::start(&deterministic).await;
// let client_a = server.create_client(cx_a, "user_a").await;
// let client_b = server.create_client(cx_b, "user_b").await;
use gpui::{BackgroundExecutor, TestAppContext};
use notifications::NotificationEvent;
use parking_lot::Mutex;
use rpc::{proto, Notification};
// let notification_events_a = Arc::new(Mutex::new(Vec::new()));
// let notification_events_b = Arc::new(Mutex::new(Vec::new()));
// client_a.notification_store().update(cx_a, |_, cx| {
// let events = notification_events_a.clone();
// cx.subscribe(&cx.handle(), move |_, _, event, _| {
// events.lock().push(event.clone());
// })
// .detach()
// });
// client_b.notification_store().update(cx_b, |_, cx| {
// let events = notification_events_b.clone();
// cx.subscribe(&cx.handle(), move |_, _, event, _| {
// events.lock().push(event.clone());
// })
// .detach()
// });
use crate::tests::TestServer;
// // Client A sends a contact request to client B.
// client_a
// .user_store()
// .update(cx_a, |store, cx| store.request_contact(client_b.id(), cx))
// .await
// .unwrap();
#[gpui::test]
async fn test_notifications(
executor: BackgroundExecutor,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
let mut server = TestServer::start(executor.clone()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
// // Client B receives a contact request notification and responds to the
// // request, accepting it.
// deterministic.run_until_parked();
// client_b.notification_store().update(cx_b, |store, cx| {
// assert_eq!(store.notification_count(), 1);
// assert_eq!(store.unread_notification_count(), 1);
let notification_events_a = Arc::new(Mutex::new(Vec::new()));
let notification_events_b = Arc::new(Mutex::new(Vec::new()));
client_a.notification_store().update(cx_a, |_, cx| {
let events = notification_events_a.clone();
cx.subscribe(&cx.handle(), move |_, _, event, _| {
events.lock().push(event.clone());
})
.detach()
});
client_b.notification_store().update(cx_b, |_, cx| {
let events = notification_events_b.clone();
cx.subscribe(&cx.handle(), move |_, _, event, _| {
events.lock().push(event.clone());
})
.detach()
});
// let entry = store.notification_at(0).unwrap();
// assert_eq!(
// entry.notification,
// Notification::ContactRequest {
// sender_id: client_a.id()
// }
// );
// assert!(!entry.is_read);
// assert_eq!(
// &notification_events_b.lock()[0..],
// &[
// NotificationEvent::NewNotification {
// entry: entry.clone(),
// },
// NotificationEvent::NotificationsUpdated {
// old_range: 0..0,
// new_count: 1
// }
// ]
// );
// Client A sends a contact request to client B.
client_a
.user_store()
.update(cx_a, |store, cx| store.request_contact(client_b.id(), cx))
.await
.unwrap();
// store.respond_to_notification(entry.notification.clone(), true, cx);
// });
// Client B receives a contact request notification and responds to the
// request, accepting it.
executor.run_until_parked();
client_b.notification_store().update(cx_b, |store, cx| {
assert_eq!(store.notification_count(), 1);
assert_eq!(store.unread_notification_count(), 1);
// // Client B sees the notification is now read, and that they responded.
// deterministic.run_until_parked();
// client_b.notification_store().read_with(cx_b, |store, _| {
// assert_eq!(store.notification_count(), 1);
// assert_eq!(store.unread_notification_count(), 0);
let entry = store.notification_at(0).unwrap();
assert_eq!(
entry.notification,
Notification::ContactRequest {
sender_id: client_a.id()
}
);
assert!(!entry.is_read);
assert_eq!(
&notification_events_b.lock()[0..],
&[
NotificationEvent::NewNotification {
entry: entry.clone(),
},
NotificationEvent::NotificationsUpdated {
old_range: 0..0,
new_count: 1
}
]
);
// let entry = store.notification_at(0).unwrap();
// assert!(entry.is_read);
// assert_eq!(entry.response, Some(true));
// assert_eq!(
// &notification_events_b.lock()[2..],
// &[
// NotificationEvent::NotificationRead {
// entry: entry.clone(),
// },
// NotificationEvent::NotificationsUpdated {
// old_range: 0..1,
// new_count: 1
// }
// ]
// );
// });
store.respond_to_notification(entry.notification.clone(), true, cx);
});
// // Client A receives a notification that client B accepted their request.
// client_a.notification_store().read_with(cx_a, |store, _| {
// assert_eq!(store.notification_count(), 1);
// assert_eq!(store.unread_notification_count(), 1);
// Client B sees the notification is now read, and that they responded.
executor.run_until_parked();
client_b.notification_store().read_with(cx_b, |store, _| {
assert_eq!(store.notification_count(), 1);
assert_eq!(store.unread_notification_count(), 0);
// let entry = store.notification_at(0).unwrap();
// assert_eq!(
// entry.notification,
// Notification::ContactRequestAccepted {
// responder_id: client_b.id()
// }
// );
// assert!(!entry.is_read);
// });
let entry = store.notification_at(0).unwrap();
assert!(entry.is_read);
assert_eq!(entry.response, Some(true));
assert_eq!(
&notification_events_b.lock()[2..],
&[
NotificationEvent::NotificationRead {
entry: entry.clone(),
},
NotificationEvent::NotificationsUpdated {
old_range: 0..1,
new_count: 1
}
]
);
});
// // Client A creates a channel and invites client B to be a member.
// let channel_id = client_a
// .channel_store()
// .update(cx_a, |store, cx| {
// store.create_channel("the-channel", None, cx)
// })
// .await
// .unwrap();
// client_a
// .channel_store()
// .update(cx_a, |store, cx| {
// store.invite_member(channel_id, client_b.id(), proto::ChannelRole::Member, cx)
// })
// .await
// .unwrap();
// Client A receives a notification that client B accepted their request.
client_a.notification_store().read_with(cx_a, |store, _| {
assert_eq!(store.notification_count(), 1);
assert_eq!(store.unread_notification_count(), 1);
// // Client B receives a channel invitation notification and responds to the
// // invitation, accepting it.
// deterministic.run_until_parked();
// client_b.notification_store().update(cx_b, |store, cx| {
// assert_eq!(store.notification_count(), 2);
// assert_eq!(store.unread_notification_count(), 1);
let entry = store.notification_at(0).unwrap();
assert_eq!(
entry.notification,
Notification::ContactRequestAccepted {
responder_id: client_b.id()
}
);
assert!(!entry.is_read);
});
// let entry = store.notification_at(0).unwrap();
// assert_eq!(
// entry.notification,
// Notification::ChannelInvitation {
// channel_id,
// channel_name: "the-channel".to_string(),
// inviter_id: client_a.id()
// }
// );
// assert!(!entry.is_read);
// Client A creates a channel and invites client B to be a member.
let channel_id = client_a
.channel_store()
.update(cx_a, |store, cx| {
store.create_channel("the-channel", None, cx)
})
.await
.unwrap();
client_a
.channel_store()
.update(cx_a, |store, cx| {
store.invite_member(channel_id, client_b.id(), proto::ChannelRole::Member, cx)
})
.await
.unwrap();
// store.respond_to_notification(entry.notification.clone(), true, cx);
// });
// Client B receives a channel invitation notification and responds to the
// invitation, accepting it.
executor.run_until_parked();
client_b.notification_store().update(cx_b, |store, cx| {
assert_eq!(store.notification_count(), 2);
assert_eq!(store.unread_notification_count(), 1);
// // Client B sees the notification is now read, and that they responded.
// deterministic.run_until_parked();
// client_b.notification_store().read_with(cx_b, |store, _| {
// assert_eq!(store.notification_count(), 2);
// assert_eq!(store.unread_notification_count(), 0);
let entry = store.notification_at(0).unwrap();
assert_eq!(
entry.notification,
Notification::ChannelInvitation {
channel_id,
channel_name: "the-channel".to_string(),
inviter_id: client_a.id()
}
);
assert!(!entry.is_read);
// let entry = store.notification_at(0).unwrap();
// assert!(entry.is_read);
// assert_eq!(entry.response, Some(true));
// });
// }
store.respond_to_notification(entry.notification.clone(), true, cx);
});
// Client B sees the notification is now read, and that they responded.
executor.run_until_parked();
client_b.notification_store().read_with(cx_b, |store, _| {
assert_eq!(store.notification_count(), 2);
assert_eq!(store.unread_notification_count(), 0);
let entry = store.notification_at(0).unwrap();
assert!(entry.is_read);
assert_eq!(entry.response, Some(true));
});
}

View file

@ -220,14 +220,6 @@ impl RandomizedTest for RandomChannelBufferTest {
Ok(())
}
async fn on_client_added(client: &Rc<TestClient>, cx: &mut TestAppContext) {
let channel_store = client.channel_store();
while channel_store.read_with(cx, |store, _| store.channel_count() == 0) {
// todo!(notifications)
// channel_store.next_notification(cx).await;
}
}
async fn on_quiesce(server: &mut TestServer, clients: &mut [(Rc<TestClient>, TestAppContext)]) {
let channels = server.app_state.db.all_channels().await.unwrap();

View file

@ -115,7 +115,7 @@ pub trait RandomizedTest: 'static + Sized {
async fn initialize(server: &mut TestServer, users: &[UserTestPlan]);
async fn on_client_added(client: &Rc<TestClient>, cx: &mut TestAppContext);
async fn on_client_added(_client: &Rc<TestClient>, _cx: &mut TestAppContext) {}
async fn on_quiesce(server: &mut TestServer, client: &mut [(Rc<TestClient>, TestAppContext)]);
}

View file

@ -17,6 +17,7 @@ use gpui::{BackgroundExecutor, Context, Model, TestAppContext, WindowHandle};
use language::LanguageRegistry;
use node_runtime::FakeNodeRuntime;
use notifications::NotificationStore;
use parking_lot::Mutex;
use project::{Project, WorktreeId};
use rpc::{proto::ChannelRole, RECEIVE_TIMEOUT};
@ -47,8 +48,7 @@ pub struct TestClient {
pub username: String,
pub app_state: Arc<workspace::AppState>,
channel_store: Model<ChannelStore>,
// todo!(notifications)
// notification_store: Model<NotificationStore>,
notification_store: Model<NotificationStore>,
state: RefCell<TestClientState>,
}
@ -234,8 +234,7 @@ impl TestServer {
audio::init((), cx);
call::init(client.clone(), user_store.clone(), cx);
channel::init(&client, user_store.clone(), cx);
//todo(notifications)
// notifications::init(client.clone(), user_store, cx);
notifications::init(client.clone(), user_store, cx);
});
client
@ -247,8 +246,7 @@ impl TestServer {
app_state,
username: name.to_string(),
channel_store: cx.read(ChannelStore::global).clone(),
// todo!(notifications)
// notification_store: cx.read(NotificationStore::global).clone(),
notification_store: cx.read(NotificationStore::global).clone(),
state: Default::default(),
};
client.wait_for_current_user(cx).await;
@ -456,10 +454,9 @@ impl TestClient {
&self.channel_store
}
// todo!(notifications)
// pub fn notification_store(&self) -> &Model<NotificationStore> {
// &self.notification_store
// }
pub fn notification_store(&self) -> &Model<NotificationStore> {
&self.notification_store
}
pub fn user_store(&self) -> &Model<UserStore> {
&self.app_state.user_store