contacts.rs

  1use super::*;
  2
  3impl Database {
  4    pub async fn get_contacts(&self, user_id: UserId) -> Result<Vec<Contact>> {
  5        #[derive(Debug, FromQueryResult)]
  6        struct ContactWithUserBusyStatuses {
  7            user_id_a: UserId,
  8            user_id_b: UserId,
  9            a_to_b: bool,
 10            accepted: bool,
 11            should_notify: bool,
 12            user_a_busy: bool,
 13            user_b_busy: bool,
 14        }
 15
 16        self.transaction(|tx| async move {
 17            let user_a_participant = Alias::new("user_a_participant");
 18            let user_b_participant = Alias::new("user_b_participant");
 19            let mut db_contacts = contact::Entity::find()
 20                .column_as(
 21                    Expr::tbl(user_a_participant.clone(), room_participant::Column::Id)
 22                        .is_not_null(),
 23                    "user_a_busy",
 24                )
 25                .column_as(
 26                    Expr::tbl(user_b_participant.clone(), room_participant::Column::Id)
 27                        .is_not_null(),
 28                    "user_b_busy",
 29                )
 30                .filter(
 31                    contact::Column::UserIdA
 32                        .eq(user_id)
 33                        .or(contact::Column::UserIdB.eq(user_id)),
 34                )
 35                .join_as(
 36                    JoinType::LeftJoin,
 37                    contact::Relation::UserARoomParticipant.def(),
 38                    user_a_participant,
 39                )
 40                .join_as(
 41                    JoinType::LeftJoin,
 42                    contact::Relation::UserBRoomParticipant.def(),
 43                    user_b_participant,
 44                )
 45                .into_model::<ContactWithUserBusyStatuses>()
 46                .stream(&*tx)
 47                .await?;
 48
 49            let mut contacts = Vec::new();
 50            while let Some(db_contact) = db_contacts.next().await {
 51                let db_contact = db_contact?;
 52                if db_contact.user_id_a == user_id {
 53                    if db_contact.accepted {
 54                        contacts.push(Contact::Accepted {
 55                            user_id: db_contact.user_id_b,
 56                            should_notify: db_contact.should_notify && db_contact.a_to_b,
 57                            busy: db_contact.user_b_busy,
 58                        });
 59                    } else if db_contact.a_to_b {
 60                        contacts.push(Contact::Outgoing {
 61                            user_id: db_contact.user_id_b,
 62                        })
 63                    } else {
 64                        contacts.push(Contact::Incoming {
 65                            user_id: db_contact.user_id_b,
 66                            should_notify: db_contact.should_notify,
 67                        });
 68                    }
 69                } else if db_contact.accepted {
 70                    contacts.push(Contact::Accepted {
 71                        user_id: db_contact.user_id_a,
 72                        should_notify: db_contact.should_notify && !db_contact.a_to_b,
 73                        busy: db_contact.user_a_busy,
 74                    });
 75                } else if db_contact.a_to_b {
 76                    contacts.push(Contact::Incoming {
 77                        user_id: db_contact.user_id_a,
 78                        should_notify: db_contact.should_notify,
 79                    });
 80                } else {
 81                    contacts.push(Contact::Outgoing {
 82                        user_id: db_contact.user_id_a,
 83                    });
 84                }
 85            }
 86
 87            contacts.sort_unstable_by_key(|contact| contact.user_id());
 88
 89            Ok(contacts)
 90        })
 91        .await
 92    }
 93
 94    pub async fn is_user_busy(&self, user_id: UserId) -> Result<bool> {
 95        self.transaction(|tx| async move {
 96            let participant = room_participant::Entity::find()
 97                .filter(room_participant::Column::UserId.eq(user_id))
 98                .one(&*tx)
 99                .await?;
100            Ok(participant.is_some())
101        })
102        .await
103    }
104
105    pub async fn has_contact(&self, user_id_1: UserId, user_id_2: UserId) -> Result<bool> {
106        self.transaction(|tx| async move {
107            let (id_a, id_b) = if user_id_1 < user_id_2 {
108                (user_id_1, user_id_2)
109            } else {
110                (user_id_2, user_id_1)
111            };
112
113            Ok(contact::Entity::find()
114                .filter(
115                    contact::Column::UserIdA
116                        .eq(id_a)
117                        .and(contact::Column::UserIdB.eq(id_b))
118                        .and(contact::Column::Accepted.eq(true)),
119                )
120                .one(&*tx)
121                .await?
122                .is_some())
123        })
124        .await
125    }
126
127    pub async fn send_contact_request(&self, sender_id: UserId, receiver_id: UserId) -> Result<()> {
128        self.transaction(|tx| async move {
129            let (id_a, id_b, a_to_b) = if sender_id < receiver_id {
130                (sender_id, receiver_id, true)
131            } else {
132                (receiver_id, sender_id, false)
133            };
134
135            let rows_affected = contact::Entity::insert(contact::ActiveModel {
136                user_id_a: ActiveValue::set(id_a),
137                user_id_b: ActiveValue::set(id_b),
138                a_to_b: ActiveValue::set(a_to_b),
139                accepted: ActiveValue::set(false),
140                should_notify: ActiveValue::set(true),
141                ..Default::default()
142            })
143            .on_conflict(
144                OnConflict::columns([contact::Column::UserIdA, contact::Column::UserIdB])
145                    .values([
146                        (contact::Column::Accepted, true.into()),
147                        (contact::Column::ShouldNotify, false.into()),
148                    ])
149                    .action_and_where(
150                        contact::Column::Accepted.eq(false).and(
151                            contact::Column::AToB
152                                .eq(a_to_b)
153                                .and(contact::Column::UserIdA.eq(id_b))
154                                .or(contact::Column::AToB
155                                    .ne(a_to_b)
156                                    .and(contact::Column::UserIdA.eq(id_a))),
157                        ),
158                    )
159                    .to_owned(),
160            )
161            .exec_without_returning(&*tx)
162            .await?;
163
164            if rows_affected == 1 {
165                Ok(())
166            } else {
167                Err(anyhow!("contact already requested"))?
168            }
169        })
170        .await
171    }
172
173    /// Returns a bool indicating whether the removed contact had originally accepted or not
174    ///
175    /// Deletes the contact identified by the requester and responder ids, and then returns
176    /// whether the deleted contact had originally accepted or was a pending contact request.
177    ///
178    /// # Arguments
179    ///
180    /// * `requester_id` - The user that initiates this request
181    /// * `responder_id` - The user that will be removed
182    pub async fn remove_contact(&self, requester_id: UserId, responder_id: UserId) -> Result<bool> {
183        self.transaction(|tx| async move {
184            let (id_a, id_b) = if responder_id < requester_id {
185                (responder_id, requester_id)
186            } else {
187                (requester_id, responder_id)
188            };
189
190            let contact = contact::Entity::find()
191                .filter(
192                    contact::Column::UserIdA
193                        .eq(id_a)
194                        .and(contact::Column::UserIdB.eq(id_b)),
195                )
196                .one(&*tx)
197                .await?
198                .ok_or_else(|| anyhow!("no such contact"))?;
199
200            contact::Entity::delete_by_id(contact.id).exec(&*tx).await?;
201            Ok(contact.accepted)
202        })
203        .await
204    }
205
206    pub async fn dismiss_contact_notification(
207        &self,
208        user_id: UserId,
209        contact_user_id: UserId,
210    ) -> Result<()> {
211        self.transaction(|tx| async move {
212            let (id_a, id_b, a_to_b) = if user_id < contact_user_id {
213                (user_id, contact_user_id, true)
214            } else {
215                (contact_user_id, user_id, false)
216            };
217
218            let result = contact::Entity::update_many()
219                .set(contact::ActiveModel {
220                    should_notify: ActiveValue::set(false),
221                    ..Default::default()
222                })
223                .filter(
224                    contact::Column::UserIdA
225                        .eq(id_a)
226                        .and(contact::Column::UserIdB.eq(id_b))
227                        .and(
228                            contact::Column::AToB
229                                .eq(a_to_b)
230                                .and(contact::Column::Accepted.eq(true))
231                                .or(contact::Column::AToB
232                                    .ne(a_to_b)
233                                    .and(contact::Column::Accepted.eq(false))),
234                        ),
235                )
236                .exec(&*tx)
237                .await?;
238            if result.rows_affected == 0 {
239                Err(anyhow!("no such contact request"))?
240            } else {
241                Ok(())
242            }
243        })
244        .await
245    }
246
247    pub async fn respond_to_contact_request(
248        &self,
249        responder_id: UserId,
250        requester_id: UserId,
251        accept: bool,
252    ) -> Result<()> {
253        self.transaction(|tx| async move {
254            let (id_a, id_b, a_to_b) = if responder_id < requester_id {
255                (responder_id, requester_id, false)
256            } else {
257                (requester_id, responder_id, true)
258            };
259            let rows_affected = if accept {
260                let result = contact::Entity::update_many()
261                    .set(contact::ActiveModel {
262                        accepted: ActiveValue::set(true),
263                        should_notify: ActiveValue::set(true),
264                        ..Default::default()
265                    })
266                    .filter(
267                        contact::Column::UserIdA
268                            .eq(id_a)
269                            .and(contact::Column::UserIdB.eq(id_b))
270                            .and(contact::Column::AToB.eq(a_to_b)),
271                    )
272                    .exec(&*tx)
273                    .await?;
274                result.rows_affected
275            } else {
276                let result = contact::Entity::delete_many()
277                    .filter(
278                        contact::Column::UserIdA
279                            .eq(id_a)
280                            .and(contact::Column::UserIdB.eq(id_b))
281                            .and(contact::Column::AToB.eq(a_to_b))
282                            .and(contact::Column::Accepted.eq(false)),
283                    )
284                    .exec(&*tx)
285                    .await?;
286
287                result.rows_affected
288            };
289
290            if rows_affected == 1 {
291                Ok(())
292            } else {
293                Err(anyhow!("no such contact request"))?
294            }
295        })
296        .await
297    }
298}