use crate::{ apub::actions::{ AnnounceSubmission, CreateSubmission, DeleteComment, DeleteSubmission, UpdateSubmission, }, Action, Outbound, Context, Error, Required, }; use std::collections::HashSet; impl Action for CreateSubmission { fn perform(&self, context: &Context) -> Result>, Error> { let submission = context .store .submissions .create(self.profile_id, &self.title, self.visibility)?; let submission_id = submission.id(); let mut changes = submission.update(); changes.publish(Some(self.published)); if let Some(description) = &self.description { changes.description(description); } let submission = context.store.submissions.update(&changes)?; let mut changes = submission.update_files(); for file in &self.files { if let Ok(Some(file)) = context.store.files.by_id(*file) { changes.add_file(&file); } } let submission = context.store.submissions.update_files(&changes)?; if let Some(apub_id) = &self.note_apub_id { context.apub.submission(apub_id, submission_id)?; } let profile_id = self.profile_id; let published = submission.published().req()?; let context_clone = context.clone(); context.spawn_blocking(move || { for follower_id in context_clone.store.view.follows.forward_iter(profile_id) { if let Ok(Some(true)) = context_clone.store.profiles.is_local(follower_id) { context_clone .store .view .submissions .new(follower_id, submission_id, published); } } }); if context.is_local(submission.profile_id())? { return Ok(Some(Box::new(crate::apub::results::SubmissionCreated { submission_id, }))); } Ok(None) } } impl Action for AnnounceSubmission { fn perform(&self, _: &Context) -> Result>, Error> { Ok(None) } } impl Action for UpdateSubmission { fn perform(&self, context: &Context) -> Result>, Error> { let submission_id = self.submission_id; let submission = context.store.submissions.by_id(submission_id)?.req()?; let mut changes = submission.update(); if let Some(title) = &self.title { changes.title(title); } if let Some(description) = &self.description { changes.description(description); } let submission = context.store.submissions.update(&changes)?; let mut original_files = submission.files().iter().cloned().collect::>(); let mut changes = submission.update_files(); for file in &self.files { original_files.remove(file); if let Ok(Some(file)) = context.store.files.by_id(*file) { changes.add_file(&file); } } context.store.submissions.update_files(&changes)?; for file in original_files { context.spawner.purge_file(file); } let profile_id = submission.profile_id(); if context.is_local(profile_id)? { return Ok(Some(Box::new(crate::apub::results::SubmissionUpdated { submission_id, }))); } Ok(None) } } fn delete_comment(comment_id: uuid::Uuid, context: &Context) -> Result<(), Error> { let note_apub_id = context.apub.apub_for_comment(comment_id)?.req()?; Action::perform( &DeleteComment { note_apub_id, comment_id, }, context, )?; Ok(()) } impl Action for DeleteSubmission { fn perform(&self, context: &Context) -> Result>, Error> { let submission_id = self.submission_id; let opt = context.store.submissions.delete(submission_id)?; if let Some(undo_submission) = opt { let note_apub_id = context.apub.apub_for_submission(submission_id)?.req()?; context.apub.delete_object(¬e_apub_id)?; let context_clone = context.clone(); context.spawn_blocking(move || { for comment_id in context_clone.store.comments.for_submission(submission_id) { if let Err(e) = delete_comment(comment_id, &context_clone) { log::error!("Failed to delete comment {}: {}", comment_id, e); } } }); for file_id in undo_submission.0.files() { context.spawner.purge_file(*file_id); } let context_clone = context.clone(); context.spawn_blocking(move || { context_clone.store.view.submissions.remove(submission_id); }); let profile_id = undo_submission.0.profile_id(); if context.is_local(profile_id)? { return Ok(Some(Box::new(crate::apub::results::SubmissionDeleted { profile_id, submission_id, }))); } } Ok(None) } }