Merge pull request #2077 from zed-industries/2064-remove-contacts

Remove contact from contact list
This commit is contained in:
Petros Amoiridis 2023-01-26 20:04:15 +02:00 committed by GitHub
commit 489be5e77b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 92 additions and 25 deletions

View file

@ -595,7 +595,16 @@ impl Database {
.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 {
let (id_a, id_b) = if responder_id < requester_id {
(responder_id, requester_id)
@ -603,20 +612,18 @@ impl Database {
(requester_id, responder_id)
};
let result = contact::Entity::delete_many()
let contact = contact::Entity::find()
.filter(
contact::Column::UserIdA
.eq(id_a)
.and(contact::Column::UserIdB.eq(id_b)),
)
.exec(&*tx)
.await?;
.one(&*tx)
.await?
.ok_or_else(|| anyhow!("no such contact"))?;
if result.rows_affected == 1 {
Ok(())
} else {
Err(anyhow!("no such contact"))?
}
contact::Entity::delete_by_id(contact.id).exec(&*tx).await?;
Ok(contact.accepted)
})
.await
}

View file

@ -1961,23 +1961,31 @@ async fn remove_contact(
let requester_id = session.user_id;
let responder_id = UserId::from_proto(request.user_id);
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;
// Update outgoing contact requests of requester
let mut update = proto::UpdateContacts::default();
update
.remove_outgoing_requests
.push(responder_id.to_proto());
if contact_accepted {
update.remove_contacts.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) {
session.peer.send(connection_id, update.clone())?;
}
// Update incoming contact requests of responder
let mut update = proto::UpdateContacts::default();
update
.remove_incoming_requests
.push(requester_id.to_proto());
if contact_accepted {
update.remove_contacts.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) {
session.peer.send(connection_id, update.clone())?;
}

View file

@ -5291,6 +5291,27 @@ async fn test_contacts(
[("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(
client: &TestClient,
cx: &TestAppContext,

View file

@ -1,22 +1,22 @@
use std::{mem, sync::Arc};
use crate::contacts_popover;
use call::ActiveCall;
use client::{proto::PeerId, Contact, User, UserStore};
use editor::{Cancel, Editor};
use futures::StreamExt;
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{
elements::*,
geometry::{rect::RectF, vector::vec2f},
impl_actions, impl_internal_actions,
keymap_matcher::KeymapContext,
AppContext, CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext,
Subscription, View, ViewContext, ViewHandle,
AppContext, CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, PromptLevel,
RenderContext, Subscription, View, ViewContext, ViewHandle,
};
use menu::{Confirm, SelectNext, SelectPrev};
use project::Project;
use serde::Deserialize;
use settings::Settings;
use std::{mem, sync::Arc};
use theme::IconButton;
use util::ResultExt;
use workspace::{JoinProject, OpenSharedScreen};
@ -299,9 +299,19 @@ impl ContactList {
}
fn remove_contact(&mut self, request: &RemoveContact, cx: &mut ViewContext<Self>) {
self.user_store
.update(cx, |store, cx| store.remove_contact(request.0, cx))
.detach();
let user_id = request.0;
let user_store = self.user_store.clone();
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(
@ -1051,7 +1061,7 @@ impl ContactList {
let user_id = contact.user.id;
let initial_project = project.clone();
let mut element =
MouseEventHandler::<Contact>::new(contact.user.id as usize, cx, |_, _| {
MouseEventHandler::<Contact>::new(contact.user.id as usize, cx, |_, cx| {
Flex::row()
.with_children(contact.user.avatar.clone().map(|avatar| {
let status_badge = if contact.online {
@ -1093,6 +1103,27 @@ impl ContactList {
.flex(1., true)
.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 {
Some(
Label::new("Calling".to_string(), theme.calling_indicator.text.clone())

View file

@ -48,7 +48,7 @@ impl View for ContactNotification {
ContactEventKind::Requested => render_user_notification(
self.user.clone(),
"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),
vec![
(