use crate::events::errors::EventProcessorError;
use crate::events::retry::event::RetryEvent;
use crate::handle_indexing_results;
use nexus_common::db::kv::JsonAction;
use nexus_common::db::OperationOutcome;
use nexus_common::models::follow::{Followers, Following, Friends, UserFollows};
use nexus_common::models::notification::Notification;
use nexus_common::models::user::UserCounts;
use nexus_common::types::DynError;
use pubky_app_specs::{user_uri_builder, PubkyId};
use tracing::debug;
pub async fn sync_put(follower_id: PubkyId, followee_id: PubkyId) -> Result<(), DynError> {
debug!("Indexing new follow: {} -> {}", follower_id, followee_id);
match Followers::put_to_graph(&follower_id, &followee_id).await? {
OperationOutcome::Updated => return Ok(()),
OperationOutcome::MissingDependency => {
if let Some(key) =
RetryEvent::generate_index_key(&user_uri_builder(followee_id.to_string()))
{
let dependency = vec![key];
return Err(EventProcessorError::MissingDependency { dependency }.into());
}
}
OperationOutcome::CreatedOrDeleted => {
let will_be_friends =
is_followee_following_follower(&follower_id, &followee_id).await?;
let followers = Followers(vec![follower_id.to_string()]);
let following = Following(vec![followee_id.to_string()]);
let indexing_results = tokio::join!(
followers.put_to_index(&followee_id),
following.put_to_index(&follower_id),
update_follow_counts(
&follower_id,
&followee_id,
JsonAction::Increment(1),
will_be_friends
),
Notification::new_follow(&follower_id, &followee_id, will_be_friends)
);
handle_indexing_results!(
indexing_results.0,
indexing_results.1,
indexing_results.2,
indexing_results.3
);
}
};
Ok(())
}
pub async fn del(follower_id: PubkyId, followee_id: PubkyId) -> Result<(), DynError> {
debug!("Deleting follow: {} -> {}", follower_id, followee_id);
sync_del(follower_id, followee_id).await
}
pub async fn sync_del(follower_id: PubkyId, followee_id: PubkyId) -> Result<(), DynError> {
match Followers::del_from_graph(&follower_id, &followee_id).await? {
OperationOutcome::Updated => Ok(()),
OperationOutcome::MissingDependency => Err(EventProcessorError::SkipIndexing.into()),
OperationOutcome::CreatedOrDeleted => {
let were_friends = Friends::check(&follower_id, &followee_id).await?;
let followers = Followers(vec![follower_id.to_string()]);
let following = Following(vec![followee_id.to_string()]);
let indexing_results = tokio::join!(
followers.del_from_index(&followee_id),
following.del_from_index(&follower_id),
update_follow_counts(
&follower_id,
&followee_id,
JsonAction::Decrement(1),
were_friends,
),
Notification::lost_follow(&follower_id, &followee_id, were_friends)
);
handle_indexing_results!(
indexing_results.0,
indexing_results.1,
indexing_results.2,
indexing_results.3
);
Ok(())
}
}
}
async fn update_follow_counts(
follower_id: &str,
followee_id: &str,
counter: JsonAction,
update_friend_relationship: bool,
) -> Result<(), DynError> {
UserCounts::update_index_field(follower_id, "following", counter.clone()).await?;
UserCounts::update(followee_id, "followers", counter.clone(), None).await?;
if update_friend_relationship {
UserCounts::update_index_field(follower_id, "friends", counter.clone()).await?;
UserCounts::update_index_field(followee_id, "friends", counter.clone()).await?;
}
Ok(())
}
pub async fn is_followee_following_follower(
user_a_id: &str,
user_b_id: &str,
) -> Result<bool, DynError> {
let (a_follows_b, b_follows_a) = tokio::try_join!(
Following::check(user_a_id, user_b_id),
Following::check(user_b_id, user_a_id),
)?;
Ok(!a_follows_b && b_follows_a)
}