Merge pull request #2077 from zed-industries/2064-remove-contacts
Remove contact from contact list
This commit is contained in:
commit
489be5e77b
5 changed files with 92 additions and 25 deletions
|
@ -595,7 +595,16 @@ impl Database {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_contact(&self, requester_id: UserId, responder_id: UserId) -> Result<()> {
|
/// Returns a bool indicating whether the removed contact had originally accepted or not
|
||||||
|
///
|
||||||
|
/// Deletes the contact identified by the requester and responder ids, and then returns
|
||||||
|
/// whether the deleted contact had originally accepted or was a pending contact request.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `requester_id` - The user that initiates this request
|
||||||
|
/// * `responder_id` - The user that will be removed
|
||||||
|
pub async fn remove_contact(&self, requester_id: UserId, responder_id: UserId) -> Result<bool> {
|
||||||
self.transaction(|tx| async move {
|
self.transaction(|tx| async move {
|
||||||
let (id_a, id_b) = if responder_id < requester_id {
|
let (id_a, id_b) = if responder_id < requester_id {
|
||||||
(responder_id, requester_id)
|
(responder_id, requester_id)
|
||||||
|
@ -603,20 +612,18 @@ impl Database {
|
||||||
(requester_id, responder_id)
|
(requester_id, responder_id)
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = contact::Entity::delete_many()
|
let contact = contact::Entity::find()
|
||||||
.filter(
|
.filter(
|
||||||
contact::Column::UserIdA
|
contact::Column::UserIdA
|
||||||
.eq(id_a)
|
.eq(id_a)
|
||||||
.and(contact::Column::UserIdB.eq(id_b)),
|
.and(contact::Column::UserIdB.eq(id_b)),
|
||||||
)
|
)
|
||||||
.exec(&*tx)
|
.one(&*tx)
|
||||||
.await?;
|
.await?
|
||||||
|
.ok_or_else(|| anyhow!("no such contact"))?;
|
||||||
|
|
||||||
if result.rows_affected == 1 {
|
contact::Entity::delete_by_id(contact.id).exec(&*tx).await?;
|
||||||
Ok(())
|
Ok(contact.accepted)
|
||||||
} else {
|
|
||||||
Err(anyhow!("no such contact"))?
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1961,23 +1961,31 @@ async fn remove_contact(
|
||||||
let requester_id = session.user_id;
|
let requester_id = session.user_id;
|
||||||
let responder_id = UserId::from_proto(request.user_id);
|
let responder_id = UserId::from_proto(request.user_id);
|
||||||
let db = session.db().await;
|
let db = session.db().await;
|
||||||
db.remove_contact(requester_id, responder_id).await?;
|
let contact_accepted = db.remove_contact(requester_id, responder_id).await?;
|
||||||
|
|
||||||
let pool = session.connection_pool().await;
|
let pool = session.connection_pool().await;
|
||||||
// Update outgoing contact requests of requester
|
// Update outgoing contact requests of requester
|
||||||
let mut update = proto::UpdateContacts::default();
|
let mut update = proto::UpdateContacts::default();
|
||||||
update
|
if contact_accepted {
|
||||||
.remove_outgoing_requests
|
update.remove_contacts.push(responder_id.to_proto());
|
||||||
.push(responder_id.to_proto());
|
} else {
|
||||||
|
update
|
||||||
|
.remove_outgoing_requests
|
||||||
|
.push(responder_id.to_proto());
|
||||||
|
}
|
||||||
for connection_id in pool.user_connection_ids(requester_id) {
|
for connection_id in pool.user_connection_ids(requester_id) {
|
||||||
session.peer.send(connection_id, update.clone())?;
|
session.peer.send(connection_id, update.clone())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update incoming contact requests of responder
|
// Update incoming contact requests of responder
|
||||||
let mut update = proto::UpdateContacts::default();
|
let mut update = proto::UpdateContacts::default();
|
||||||
update
|
if contact_accepted {
|
||||||
.remove_incoming_requests
|
update.remove_contacts.push(requester_id.to_proto());
|
||||||
.push(requester_id.to_proto());
|
} else {
|
||||||
|
update
|
||||||
|
.remove_incoming_requests
|
||||||
|
.push(requester_id.to_proto());
|
||||||
|
}
|
||||||
for connection_id in pool.user_connection_ids(responder_id) {
|
for connection_id in pool.user_connection_ids(responder_id) {
|
||||||
session.peer.send(connection_id, update.clone())?;
|
session.peer.send(connection_id, update.clone())?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5291,6 +5291,27 @@ async fn test_contacts(
|
||||||
[("user_b".to_string(), "online", "free")]
|
[("user_b".to_string(), "online", "free")]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Test removing a contact
|
||||||
|
client_b
|
||||||
|
.user_store
|
||||||
|
.update(cx_b, |store, cx| {
|
||||||
|
store.remove_contact(client_c.user_id().unwrap(), cx)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
deterministic.run_until_parked();
|
||||||
|
assert_eq!(
|
||||||
|
contacts(&client_b, cx_b),
|
||||||
|
[
|
||||||
|
("user_a".to_string(), "offline", "free"),
|
||||||
|
("user_d".to_string(), "online", "free")
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
contacts(&client_c, cx_c),
|
||||||
|
[("user_a".to_string(), "offline", "free"),]
|
||||||
|
);
|
||||||
|
|
||||||
fn contacts(
|
fn contacts(
|
||||||
client: &TestClient,
|
client: &TestClient,
|
||||||
cx: &TestAppContext,
|
cx: &TestAppContext,
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
use std::{mem, sync::Arc};
|
|
||||||
|
|
||||||
use crate::contacts_popover;
|
use crate::contacts_popover;
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use client::{proto::PeerId, Contact, User, UserStore};
|
use client::{proto::PeerId, Contact, User, UserStore};
|
||||||
use editor::{Cancel, Editor};
|
use editor::{Cancel, Editor};
|
||||||
|
use futures::StreamExt;
|
||||||
use fuzzy::{match_strings, StringMatchCandidate};
|
use fuzzy::{match_strings, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*,
|
elements::*,
|
||||||
geometry::{rect::RectF, vector::vec2f},
|
geometry::{rect::RectF, vector::vec2f},
|
||||||
impl_actions, impl_internal_actions,
|
impl_actions, impl_internal_actions,
|
||||||
keymap_matcher::KeymapContext,
|
keymap_matcher::KeymapContext,
|
||||||
AppContext, CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext,
|
AppContext, CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, PromptLevel,
|
||||||
Subscription, View, ViewContext, ViewHandle,
|
RenderContext, Subscription, View, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use menu::{Confirm, SelectNext, SelectPrev};
|
use menu::{Confirm, SelectNext, SelectPrev};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
use std::{mem, sync::Arc};
|
||||||
use theme::IconButton;
|
use theme::IconButton;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{JoinProject, OpenSharedScreen};
|
use workspace::{JoinProject, OpenSharedScreen};
|
||||||
|
@ -299,9 +299,19 @@ impl ContactList {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_contact(&mut self, request: &RemoveContact, cx: &mut ViewContext<Self>) {
|
fn remove_contact(&mut self, request: &RemoveContact, cx: &mut ViewContext<Self>) {
|
||||||
self.user_store
|
let user_id = request.0;
|
||||||
.update(cx, |store, cx| store.remove_contact(request.0, cx))
|
let user_store = self.user_store.clone();
|
||||||
.detach();
|
let prompt_message = "Are you sure you want to remove this contact?";
|
||||||
|
let mut answer = cx.prompt(PromptLevel::Warning, prompt_message, &["Remove", "Cancel"]);
|
||||||
|
cx.spawn(|_, mut cx| async move {
|
||||||
|
if answer.next().await == Some(0) {
|
||||||
|
user_store
|
||||||
|
.update(&mut cx, |store, cx| store.remove_contact(user_id, cx))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn respond_to_contact_request(
|
fn respond_to_contact_request(
|
||||||
|
@ -1051,7 +1061,7 @@ impl ContactList {
|
||||||
let user_id = contact.user.id;
|
let user_id = contact.user.id;
|
||||||
let initial_project = project.clone();
|
let initial_project = project.clone();
|
||||||
let mut element =
|
let mut element =
|
||||||
MouseEventHandler::<Contact>::new(contact.user.id as usize, cx, |_, _| {
|
MouseEventHandler::<Contact>::new(contact.user.id as usize, cx, |_, cx| {
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_children(contact.user.avatar.clone().map(|avatar| {
|
.with_children(contact.user.avatar.clone().map(|avatar| {
|
||||||
let status_badge = if contact.online {
|
let status_badge = if contact.online {
|
||||||
|
@ -1093,6 +1103,27 @@ impl ContactList {
|
||||||
.flex(1., true)
|
.flex(1., true)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
|
.with_child(
|
||||||
|
MouseEventHandler::<Cancel>::new(
|
||||||
|
contact.user.id as usize,
|
||||||
|
cx,
|
||||||
|
|mouse_state, _| {
|
||||||
|
let button_style =
|
||||||
|
theme.contact_button.style_for(mouse_state, false);
|
||||||
|
render_icon_button(button_style, "icons/x_mark_8.svg")
|
||||||
|
.aligned()
|
||||||
|
.flex_float()
|
||||||
|
.boxed()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_padding(Padding::uniform(2.))
|
||||||
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
|
.on_click(MouseButton::Left, move |_, cx| {
|
||||||
|
cx.dispatch_action(RemoveContact(user_id))
|
||||||
|
})
|
||||||
|
.flex_float()
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
.with_children(if calling {
|
.with_children(if calling {
|
||||||
Some(
|
Some(
|
||||||
Label::new("Calling".to_string(), theme.calling_indicator.text.clone())
|
Label::new("Calling".to_string(), theme.calling_indicator.text.clone())
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl View for ContactNotification {
|
||||||
ContactEventKind::Requested => render_user_notification(
|
ContactEventKind::Requested => render_user_notification(
|
||||||
self.user.clone(),
|
self.user.clone(),
|
||||||
"wants to add you as a contact",
|
"wants to add you as a contact",
|
||||||
Some("They won't know if you decline."),
|
Some("They won't be alerted if you decline."),
|
||||||
Dismiss(self.user.id),
|
Dismiss(self.user.id),
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue