
There's still a bit more work to do on this, but this PR is compiling (with warnings) after eliminating the key types. When the tasks below are complete, this will be the new narrative for GPUI: - `Entity<T>` - This replaces `View<T>`/`Model<T>`. It represents a unit of state, and if `T` implements `Render`, then `Entity<T>` implements `Element`. - `&mut App` This replaces `AppContext` and represents the app. - `&mut Context<T>` This replaces `ModelContext` and derefs to `App`. It is provided by the framework when updating an entity. - `&mut Window` Broken out of `&mut WindowContext` which no longer exists. Every method that once took `&mut WindowContext` now takes `&mut Window, &mut App` and every method that took `&mut ViewContext<T>` now takes `&mut Window, &mut Context<T>` Not pictured here are the two other failed attempts. It's been quite a month! Tasks: - [x] Remove `View`, `ViewContext`, `WindowContext` and thread through `Window` - [x] [@cole-miller @mikayla-maki] Redraw window when entities change - [x] [@cole-miller @mikayla-maki] Get examples and Zed running - [x] [@cole-miller @mikayla-maki] Fix Zed rendering - [x] [@mikayla-maki] Fix todo! macros and comments - [x] Fix a bug where the editor would not be redrawn because of view caching - [x] remove publicness window.notify() and replace with `AppContext::notify` - [x] remove `observe_new_window_models`, replace with `observe_new_models` with an optional window - [x] Fix a bug where the project panel would not be redrawn because of the wrong refresh() call being used - [x] Fix the tests - [x] Fix warnings by eliminating `Window` params or using `_` - [x] Fix conflicts - [x] Simplify generic code where possible - [x] Rename types - [ ] Update docs ### issues post merge - [x] Issues switching between normal and insert mode - [x] Assistant re-rendering failure - [x] Vim test failures - [x] Mac build issue Release Notes: - N/A --------- Co-authored-by: Antonio Scandurra <me@as-cii.com> Co-authored-by: Cole Miller <cole@zed.dev> Co-authored-by: Mikayla <mikayla@zed.dev> Co-authored-by: Joseph <joseph@zed.dev> Co-authored-by: max <max@zed.dev> Co-authored-by: Michael Sloan <michael@zed.dev> Co-authored-by: Mikayla Maki <mikaylamaki@Mikaylas-MacBook-Pro.local> Co-authored-by: Mikayla <mikayla.c.maki@gmail.com> Co-authored-by: joão <joao@zed.dev>
725 lines
21 KiB
Rust
725 lines
21 KiB
Rust
use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
|
|
use channel::{ChannelChat, ChannelMessageId, MessageParams};
|
|
use collab_ui::chat_panel::ChatPanel;
|
|
use gpui::{BackgroundExecutor, Entity, TestAppContext};
|
|
use rpc::Notification;
|
|
use workspace::dock::Panel;
|
|
|
|
#[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_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())],
|
|
reply_to_message_id: None,
|
|
},
|
|
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();
|
|
|
|
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(),
|
|
);
|
|
});
|
|
}
|
|
|
|
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: channel_id.0,
|
|
}
|
|
);
|
|
assert_eq!(
|
|
store.notification_at(1).unwrap().notification,
|
|
Notification::ChannelInvitation {
|
|
channel_id: channel_id.0,
|
|
channel_name: "the-channel".to_string(),
|
|
inviter_id: client_a.id()
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_rejoin_channel_chat(
|
|
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;
|
|
|
|
let channel_id = server
|
|
.make_channel(
|
|
"the-channel",
|
|
None,
|
|
(&client_a, cx_a),
|
|
&mut [(&client_b, cx_b)],
|
|
)
|
|
.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();
|
|
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
channel_chat_b
|
|
.update(cx_b, |c, cx| c.send_message("two".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
|
|
server.forbid_connections();
|
|
server.disconnect_client(client_a.peer_id().unwrap());
|
|
|
|
// While client A is disconnected, clients A and B both send new messages.
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| c.send_message("three".into(), cx).unwrap())
|
|
.await
|
|
.unwrap_err();
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| c.send_message("four".into(), cx).unwrap())
|
|
.await
|
|
.unwrap_err();
|
|
channel_chat_b
|
|
.update(cx_b, |c, cx| c.send_message("five".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
channel_chat_b
|
|
.update(cx_b, |c, cx| c.send_message("six".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
|
|
// Client A reconnects.
|
|
server.allow_connections();
|
|
executor.advance_clock(RECONNECT_TIMEOUT);
|
|
|
|
// Client A fetches the messages that were sent while they were disconnected
|
|
// and resends their own messages which failed to send.
|
|
let expected_messages = &["one", "two", "five", "six", "three", "four"];
|
|
assert_messages(&channel_chat_a, expected_messages, cx_a);
|
|
assert_messages(&channel_chat_b, expected_messages, cx_b);
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_remove_channel_message(
|
|
executor: BackgroundExecutor,
|
|
cx_a: &mut TestAppContext,
|
|
cx_b: &mut TestAppContext,
|
|
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_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();
|
|
|
|
// Client A sends some messages.
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
let msg_id_2 = channel_chat_a
|
|
.update(cx_a, |c, cx| {
|
|
c.send_message(
|
|
MessageParams {
|
|
text: "two @user_b".to_string(),
|
|
mentions: vec![(4..12, client_b.id())],
|
|
reply_to_message_id: None,
|
|
},
|
|
cx,
|
|
)
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.unwrap();
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| c.send_message("three".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
|
|
// Clients A and B see all of the messages.
|
|
executor.run_until_parked();
|
|
let expected_messages = &["one", "two @user_b", "three"];
|
|
assert_messages(&channel_chat_a, expected_messages, cx_a);
|
|
assert_messages(&channel_chat_b, expected_messages, cx_b);
|
|
|
|
// Ensure that client B received a notification for the mention.
|
|
client_b.notification_store().read_with(cx_b, |store, _| {
|
|
assert_eq!(store.notification_count(), 2);
|
|
let entry = store.notification_at(0).unwrap();
|
|
assert_eq!(
|
|
entry.notification,
|
|
Notification::ChannelMessageMention {
|
|
message_id: msg_id_2,
|
|
sender_id: client_a.id(),
|
|
channel_id: channel_id.0,
|
|
}
|
|
);
|
|
});
|
|
|
|
// Client A deletes one of their messages.
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| {
|
|
let ChannelMessageId::Saved(id) = c.message(1).id else {
|
|
panic!("message not saved")
|
|
};
|
|
c.remove_message(id, cx)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
// Client B sees that the message is gone.
|
|
executor.run_until_parked();
|
|
let expected_messages = &["one", "three"];
|
|
assert_messages(&channel_chat_a, expected_messages, cx_a);
|
|
assert_messages(&channel_chat_b, expected_messages, cx_b);
|
|
|
|
// Client C joins the channel chat, and does not see the deleted message.
|
|
let channel_chat_c = client_c
|
|
.channel_store()
|
|
.update(cx_c, |store, cx| store.open_channel_chat(channel_id, cx))
|
|
.await
|
|
.unwrap();
|
|
assert_messages(&channel_chat_c, expected_messages, cx_c);
|
|
|
|
// Ensure we remove the notifications when the message is removed
|
|
client_b.notification_store().read_with(cx_b, |store, _| {
|
|
// First notification is the channel invitation, second would be the mention
|
|
// notification, which should now be removed.
|
|
assert_eq!(store.notification_count(), 1);
|
|
});
|
|
}
|
|
|
|
#[track_caller]
|
|
fn assert_messages(chat: &Entity<ChannelChat>, messages: &[&str], cx: &mut TestAppContext) {
|
|
assert_eq!(
|
|
chat.read_with(cx, |chat, _| {
|
|
chat.messages()
|
|
.iter()
|
|
.map(|m| m.body.clone())
|
|
.collect::<Vec<_>>()
|
|
}),
|
|
messages
|
|
);
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_channel_message_changes(
|
|
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;
|
|
|
|
let channel_id = server
|
|
.make_channel(
|
|
"the-channel",
|
|
None,
|
|
(&client_a, cx_a),
|
|
&mut [(&client_b, cx_b)],
|
|
)
|
|
.await;
|
|
|
|
// Client A sends a message, client B should see that there is a new message.
|
|
let channel_chat_a = client_a
|
|
.channel_store()
|
|
.update(cx_a, |store, cx| store.open_channel_chat(channel_id, cx))
|
|
.await
|
|
.unwrap();
|
|
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
|
|
executor.run_until_parked();
|
|
|
|
let b_has_messages = cx_b.update(|cx| {
|
|
client_b
|
|
.channel_store()
|
|
.read(cx)
|
|
.has_new_messages(channel_id)
|
|
});
|
|
|
|
assert!(b_has_messages);
|
|
|
|
// Opening the chat should clear the changed flag.
|
|
cx_b.update(|cx| {
|
|
collab_ui::init(&client_b.app_state, cx);
|
|
});
|
|
let project_b = client_b.build_empty_local_project(cx_b);
|
|
let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
|
|
|
|
let chat_panel_b = workspace_b.update_in(cx_b, ChatPanel::new);
|
|
chat_panel_b
|
|
.update_in(cx_b, |chat_panel, window, cx| {
|
|
chat_panel.set_active(true, window, cx);
|
|
chat_panel.select_channel(channel_id, None, cx)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
executor.run_until_parked();
|
|
|
|
let b_has_messages = cx_b.update(|_, cx| {
|
|
client_b
|
|
.channel_store()
|
|
.read(cx)
|
|
.has_new_messages(channel_id)
|
|
});
|
|
|
|
assert!(!b_has_messages);
|
|
|
|
// Sending a message while the chat is open should not change the flag.
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| c.send_message("two".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
|
|
executor.run_until_parked();
|
|
|
|
let b_has_messages = cx_b.update(|_, cx| {
|
|
client_b
|
|
.channel_store()
|
|
.read(cx)
|
|
.has_new_messages(channel_id)
|
|
});
|
|
|
|
assert!(!b_has_messages);
|
|
|
|
// Sending a message while the chat is closed should change the flag.
|
|
chat_panel_b.update_in(cx_b, |chat_panel, window, cx| {
|
|
chat_panel.set_active(false, window, cx);
|
|
});
|
|
|
|
// Sending a message while the chat is open should not change the flag.
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| c.send_message("three".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
|
|
executor.run_until_parked();
|
|
|
|
let b_has_messages = cx_b.update(|_, cx| {
|
|
client_b
|
|
.channel_store()
|
|
.read(cx)
|
|
.has_new_messages(channel_id)
|
|
});
|
|
|
|
assert!(b_has_messages);
|
|
|
|
// Closing the chat should re-enable change tracking
|
|
cx_b.update(|_, _| drop(chat_panel_b));
|
|
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| c.send_message("four".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
|
|
executor.run_until_parked();
|
|
|
|
let b_has_messages = cx_b.update(|_, cx| {
|
|
client_b
|
|
.channel_store()
|
|
.read(cx)
|
|
.has_new_messages(channel_id)
|
|
});
|
|
|
|
assert!(b_has_messages);
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_chat_replies(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
|
let mut server = TestServer::start(cx_a.executor()).await;
|
|
let client_a = server.create_client(cx_a, "user_a").await;
|
|
let client_b = server.create_client(cx_b, "user_b").await;
|
|
|
|
let channel_id = server
|
|
.make_channel(
|
|
"the-channel",
|
|
None,
|
|
(&client_a, cx_a),
|
|
&mut [(&client_b, cx_b)],
|
|
)
|
|
.await;
|
|
|
|
// Client A sends a message, client B should see that there is a new message.
|
|
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 msg_id = channel_chat_a
|
|
.update(cx_a, |c, cx| c.send_message("one".into(), cx).unwrap())
|
|
.await
|
|
.unwrap();
|
|
|
|
cx_a.run_until_parked();
|
|
|
|
let reply_id = channel_chat_b
|
|
.update(cx_b, |c, cx| {
|
|
c.send_message(
|
|
MessageParams {
|
|
text: "reply".into(),
|
|
reply_to_message_id: Some(msg_id),
|
|
mentions: Vec::new(),
|
|
},
|
|
cx,
|
|
)
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx_a.run_until_parked();
|
|
|
|
channel_chat_a.update(cx_a, |channel_chat, _| {
|
|
assert_eq!(
|
|
channel_chat
|
|
.find_loaded_message(reply_id)
|
|
.unwrap()
|
|
.reply_to_message_id,
|
|
Some(msg_id),
|
|
)
|
|
});
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_chat_editing(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
|
let mut server = TestServer::start(cx_a.executor()).await;
|
|
let client_a = server.create_client(cx_a, "user_a").await;
|
|
let client_b = server.create_client(cx_b, "user_b").await;
|
|
|
|
let channel_id = server
|
|
.make_channel(
|
|
"the-channel",
|
|
None,
|
|
(&client_a, cx_a),
|
|
&mut [(&client_b, cx_b)],
|
|
)
|
|
.await;
|
|
|
|
// Client A sends a message, client B should see that there is a new message.
|
|
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 msg_id = channel_chat_a
|
|
.update(cx_a, |c, cx| {
|
|
c.send_message(
|
|
MessageParams {
|
|
text: "Initial message".into(),
|
|
reply_to_message_id: None,
|
|
mentions: Vec::new(),
|
|
},
|
|
cx,
|
|
)
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx_a.run_until_parked();
|
|
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| {
|
|
c.update_message(
|
|
msg_id,
|
|
MessageParams {
|
|
text: "Updated body".into(),
|
|
reply_to_message_id: None,
|
|
mentions: Vec::new(),
|
|
},
|
|
cx,
|
|
)
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx_a.run_until_parked();
|
|
cx_b.run_until_parked();
|
|
|
|
channel_chat_a.update(cx_a, |channel_chat, _| {
|
|
let update_message = channel_chat.find_loaded_message(msg_id).unwrap();
|
|
|
|
assert_eq!(update_message.body, "Updated body");
|
|
assert_eq!(update_message.mentions, Vec::new());
|
|
});
|
|
channel_chat_b.update(cx_b, |channel_chat, _| {
|
|
let update_message = channel_chat.find_loaded_message(msg_id).unwrap();
|
|
|
|
assert_eq!(update_message.body, "Updated body");
|
|
assert_eq!(update_message.mentions, Vec::new());
|
|
});
|
|
|
|
// test mentions are updated correctly
|
|
|
|
client_b.notification_store().read_with(cx_b, |store, _| {
|
|
assert_eq!(store.notification_count(), 1);
|
|
let entry = store.notification_at(0).unwrap();
|
|
assert!(matches!(
|
|
entry.notification,
|
|
Notification::ChannelInvitation { .. }
|
|
),);
|
|
});
|
|
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| {
|
|
c.update_message(
|
|
msg_id,
|
|
MessageParams {
|
|
text: "Updated body including a mention for @user_b".into(),
|
|
reply_to_message_id: None,
|
|
mentions: vec![(37..45, client_b.id())],
|
|
},
|
|
cx,
|
|
)
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx_a.run_until_parked();
|
|
cx_b.run_until_parked();
|
|
|
|
channel_chat_a.update(cx_a, |channel_chat, _| {
|
|
assert_eq!(
|
|
channel_chat.find_loaded_message(msg_id).unwrap().body,
|
|
"Updated body including a mention for @user_b",
|
|
)
|
|
});
|
|
channel_chat_b.update(cx_b, |channel_chat, _| {
|
|
assert_eq!(
|
|
channel_chat.find_loaded_message(msg_id).unwrap().body,
|
|
"Updated body including a mention for @user_b",
|
|
)
|
|
});
|
|
client_b.notification_store().read_with(cx_b, |store, _| {
|
|
assert_eq!(store.notification_count(), 2);
|
|
let entry = store.notification_at(0).unwrap();
|
|
assert_eq!(
|
|
entry.notification,
|
|
Notification::ChannelMessageMention {
|
|
message_id: msg_id,
|
|
sender_id: client_a.id(),
|
|
channel_id: channel_id.0,
|
|
}
|
|
);
|
|
});
|
|
|
|
// Test update message and keep the mention and check that the body is updated correctly
|
|
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| {
|
|
c.update_message(
|
|
msg_id,
|
|
MessageParams {
|
|
text: "Updated body v2 including a mention for @user_b".into(),
|
|
reply_to_message_id: None,
|
|
mentions: vec![(37..45, client_b.id())],
|
|
},
|
|
cx,
|
|
)
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx_a.run_until_parked();
|
|
cx_b.run_until_parked();
|
|
|
|
channel_chat_a.update(cx_a, |channel_chat, _| {
|
|
assert_eq!(
|
|
channel_chat.find_loaded_message(msg_id).unwrap().body,
|
|
"Updated body v2 including a mention for @user_b",
|
|
)
|
|
});
|
|
channel_chat_b.update(cx_b, |channel_chat, _| {
|
|
assert_eq!(
|
|
channel_chat.find_loaded_message(msg_id).unwrap().body,
|
|
"Updated body v2 including a mention for @user_b",
|
|
)
|
|
});
|
|
|
|
client_b.notification_store().read_with(cx_b, |store, _| {
|
|
let message = store.channel_message_for_id(msg_id);
|
|
assert!(message.is_some());
|
|
assert_eq!(
|
|
message.unwrap().body,
|
|
"Updated body v2 including a mention for @user_b"
|
|
);
|
|
assert_eq!(store.notification_count(), 2);
|
|
let entry = store.notification_at(0).unwrap();
|
|
assert_eq!(
|
|
entry.notification,
|
|
Notification::ChannelMessageMention {
|
|
message_id: msg_id,
|
|
sender_id: client_a.id(),
|
|
channel_id: channel_id.0,
|
|
}
|
|
);
|
|
});
|
|
|
|
// If we remove a mention from a message the corresponding mention notification
|
|
// should also be removed.
|
|
|
|
channel_chat_a
|
|
.update(cx_a, |c, cx| {
|
|
c.update_message(
|
|
msg_id,
|
|
MessageParams {
|
|
text: "Updated body without a mention".into(),
|
|
reply_to_message_id: None,
|
|
mentions: vec![],
|
|
},
|
|
cx,
|
|
)
|
|
.unwrap()
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx_a.run_until_parked();
|
|
cx_b.run_until_parked();
|
|
|
|
channel_chat_a.update(cx_a, |channel_chat, _| {
|
|
assert_eq!(
|
|
channel_chat.find_loaded_message(msg_id).unwrap().body,
|
|
"Updated body without a mention",
|
|
)
|
|
});
|
|
channel_chat_b.update(cx_b, |channel_chat, _| {
|
|
assert_eq!(
|
|
channel_chat.find_loaded_message(msg_id).unwrap().body,
|
|
"Updated body without a mention",
|
|
)
|
|
});
|
|
client_b.notification_store().read_with(cx_b, |store, _| {
|
|
// First notification is the channel invitation, second would be the mention
|
|
// notification, which should now be removed.
|
|
assert_eq!(store.notification_count(), 1);
|
|
});
|
|
}
|