From 52ab35d79d8aa2a2881133774c8632bd6b38af3b Mon Sep 17 00:00:00 2001
From: Nick Gerakines <12125+ngerakines@users.noreply.github.com>
Date: Thu, 22 Jan 2026 21:25:52 -0500
Subject: [PATCH] feature: post RSVP going to feed
---
src/config.rs | 2 +-
src/http/errors/mod.rs | 2 +
src/http/errors/share_bluesky_error.rs | 17 ++
src/http/errors/web_error.rs | 8 +
src/http/handle_create_rsvp.rs | 5 +
src/http/handle_share_rsvp_bluesky.rs | 165 ++++++++++++++++++
src/http/middleware_auth.rs | 2 +-
src/http/mod.rs | 1 +
src/http/server.rs | 2 +
templates/en-us/create_rsvp.partial.html | 32 ++++
.../en-us/share_rsvp_bluesky.partial.html | 21 +++
11 files changed, 255 insertions(+), 2 deletions(-)
create mode 100644 src/http/errors/share_bluesky_error.rs
create mode 100644 src/http/handle_share_rsvp_bluesky.rs
create mode 100644 templates/en-us/share_rsvp_bluesky.partial.html
diff --git a/src/config.rs b/src/config.rs
index aea3f52..5fb7899 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -204,7 +204,7 @@ impl Config {
/// Returns the OAuth scope string with the external_base interpolated
pub fn oauth_scope(&self) -> String {
format!(
- "atproto include:community.lexicon.calendar.authFull?aud=did:web:{}#smokesignal include:events.smokesignal.authFull blob:*/* account:email",
+ "atproto include:community.lexicon.calendar.authFull?aud=did:web:{}#smokesignal include:events.smokesignal.authFull include:app.bsky.authCreatePosts blob:*/* account:email",
self.external_base
)
}
diff --git a/src/http/errors/mod.rs b/src/http/errors/mod.rs
index a542c45..997b9ed 100644
--- a/src/http/errors/mod.rs
+++ b/src/http/errors/mod.rs
@@ -11,6 +11,7 @@ pub mod lfg_error;
pub mod login_error;
pub mod middleware_errors;
pub mod profile_import_error;
+pub mod share_bluesky_error;
pub mod url_error;
pub mod view_event_error;
pub mod web_error;
@@ -24,6 +25,7 @@ pub(crate) use lfg_error::LfgError;
pub(crate) use login_error::LoginError;
pub(crate) use middleware_errors::WebSessionError;
pub(crate) use profile_import_error::ProfileImportError;
+pub(crate) use share_bluesky_error::ShareBlueskyError;
pub(crate) use url_error::UrlError;
pub(crate) use view_event_error::ViewEventError;
pub(crate) use web_error::WebError;
diff --git a/src/http/errors/share_bluesky_error.rs b/src/http/errors/share_bluesky_error.rs
new file mode 100644
index 0000000..9b27f97
--- /dev/null
+++ b/src/http/errors/share_bluesky_error.rs
@@ -0,0 +1,17 @@
+use thiserror::Error;
+
+/// Represents errors that can occur when sharing an RSVP to Bluesky.
+#[derive(Debug, Error)]
+pub(crate) enum ShareBlueskyError {
+ /// Error when the AT Protocol server returns an error response.
+ #[error("error-smokesignal-share-bluesky-1 Failed to create post: {message}")]
+ PostCreationFailed { message: String },
+
+ /// Error when the event is not found.
+ #[error("error-smokesignal-share-bluesky-2 Event not found: {aturi}")]
+ EventNotFound { aturi: String },
+
+ /// Error when the user does not have a "going" RSVP to the event.
+ #[error("error-smokesignal-share-bluesky-3 No going RSVP found for event")]
+ NoGoingRsvp,
+}
diff --git a/src/http/errors/web_error.rs b/src/http/errors/web_error.rs
index b8e95ed..c724c0b 100644
--- a/src/http/errors/web_error.rs
+++ b/src/http/errors/web_error.rs
@@ -20,6 +20,7 @@ use super::import_error::ImportError;
use super::lfg_error::LfgError;
use super::login_error::LoginError;
use super::middleware_errors::MiddlewareAuthError;
+use super::share_bluesky_error::ShareBlueskyError;
use super::url_error::UrlError;
/// Represents all possible errors that can occur in the HTTP layer.
@@ -158,6 +159,13 @@ pub(crate) enum WebError {
/// such as creating, viewing, or deactivating LFG records.
#[error(transparent)]
LfgError(#[from] LfgError),
+
+ /// Share to Bluesky errors.
+ ///
+ /// This error occurs when there are issues with sharing RSVP to Bluesky,
+ /// such as post creation failures or event not found.
+ #[error(transparent)]
+ ShareBlueskyError(#[from] ShareBlueskyError),
}
/// Implementation of Axum's `IntoResponse` trait for WebError.
diff --git a/src/http/handle_create_rsvp.rs b/src/http/handle_create_rsvp.rs
index 681befc..da915f7 100644
--- a/src/http/handle_create_rsvp.rs
+++ b/src/http/handle_create_rsvp.rs
@@ -446,6 +446,9 @@ pub(crate) async fn handle_create_rsvp(
None
};
+ // Get event AT-URI for share button
+ let event_aturi = build_rsvp_form.subject_aturi.clone();
+
return Ok(RenderHtml(
&render_template,
web_context.engine.clone(),
@@ -453,6 +456,8 @@ pub(crate) async fn handle_create_rsvp(
build_rsvp_form,
event_url,
redirect_url,
+ is_going_rsvp,
+ event_aturi,
}},
)
.into_response());
diff --git a/src/http/handle_share_rsvp_bluesky.rs b/src/http/handle_share_rsvp_bluesky.rs
new file mode 100644
index 0000000..bf94881
--- /dev/null
+++ b/src/http/handle_share_rsvp_bluesky.rs
@@ -0,0 +1,165 @@
+use std::collections::HashSet;
+
+use ammonia::Builder;
+use axum::{extract::State, response::IntoResponse};
+use axum_extra::extract::{Cached, Form};
+use axum_htmx::HxRequest;
+use axum_template::RenderHtml;
+use chrono::Utc;
+use minijinja::context as template_context;
+use serde::Deserialize;
+use serde_json::json;
+
+use crate::{
+ atproto::auth::create_dpop_auth_from_session,
+ http::{
+ context::WebContext,
+ errors::{CommonError, ShareBlueskyError, WebError},
+ middleware_auth::Auth,
+ middleware_i18n::Language,
+ utils::{truncate_text, url_from_aturi},
+ },
+ storage::event::{event_get, extract_event_details, rsvp_get_by_event_and_did},
+};
+use atproto_client::com::atproto::repo::{CreateRecordRequest, CreateRecordResponse, create_record};
+
+#[derive(Deserialize)]
+pub(crate) struct ShareRsvpBlueskyRequest {
+ event_aturi: String,
+}
+
+pub(crate) async fn handle_share_rsvp_bluesky(
+ State(web_context): State The RSVP has been recorded!
{{ error_message }}
+{% endif %} +{% endif %} -- 2.51.2