Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156"use server";
import { AtpBaseClient } from "lexicons/api";import { AppBskyActorDefs, Agent as BskyAgent } from "@atproto/api";import { getIdentityData } from "actions/getIdentityData";import { restoreOAuthSession, OAuthSessionError } from "src/atproto-oauth";import { TID } from "@atproto/common";import { supabaseServerClient } from "supabase/serverClient";import { revalidatePath } from "next/cache";import { AtUri } from "@atproto/syntax";import { redirect } from "next/navigation";import { encodeActionToSearchParam } from "app/api/oauth/[route]/afterSignInActions";import { Json } from "supabase/database.types";import { IdResolver } from "@atproto/identity";import { Notification, pingIdentityToUpdateNotification,} from "src/notifications";import { v7 } from "uuid";
let leafletFeedURI = "at://did:plc:btxrwcaeyodrap5mnjw2fvmz/app.bsky.feed.generator/subscribedPublications";let idResolver = new IdResolver();
type SubscribeResult = | { success: true; hasFeed: boolean } | { success: false; error: OAuthSessionError };
export async function subscribeToPublication( publication: string, redirectRoute?: string,): Promise<SubscribeResult | never> { let identity = await getIdentityData(); if (!identity || !identity.atp_did) { return redirect( `/api/oauth/login?redirect_url=${redirectRoute}&action=${encodeActionToSearchParam({ action: "subscribe", publication })}`, ); }
const sessionResult = await restoreOAuthSession(identity.atp_did); if (!sessionResult.ok) { return { success: false, error: sessionResult.error }; } let credentialSession = sessionResult.value; let agent = new AtpBaseClient( credentialSession.fetchHandler.bind(credentialSession), ); let record = await agent.site.standard.graph.subscription.create( { repo: credentialSession.did!, rkey: TID.nextStr() }, { publication, }, ); let { error } = await supabaseServerClient .from("publication_subscriptions") .insert({ uri: record.uri, record, publication, identity: credentialSession.did!, });
// Create notification for the publication owner let publicationOwner = new AtUri(publication).host; if (publicationOwner !== credentialSession.did) { let notification: Notification = { id: v7(), recipient: publicationOwner, data: { type: "subscribe", subscription_uri: record.uri, }, }; await supabaseServerClient.from("notifications").insert(notification); await pingIdentityToUpdateNotification(publicationOwner); }
let bsky = new BskyAgent(credentialSession); let [profile, resolveDid] = await Promise.all([ bsky.app.bsky.actor.profile .get({ repo: credentialSession.did!, rkey: "self", }) .catch(), idResolver.did.resolve(credentialSession.did!), ]); if (!identity.bsky_profiles && profile.value) { await supabaseServerClient.from("bsky_profiles").insert({ did: identity.atp_did, record: profile.value as Json, handle: resolveDid?.alsoKnownAs?.[0]?.slice(5), }); } revalidatePath("/lish/[did]/[publication]", "layout"); return { success: true, hasFeed: true, };}
type UnsubscribeResult = | { success: true } | { success: false; error: OAuthSessionError };
export async function unsubscribeToPublication( publication: string,): Promise<UnsubscribeResult> { let identity = await getIdentityData(); if (!identity || !identity.atp_did) { return { success: false, error: { type: "oauth_session_expired", message: "Not authenticated", did: "", }, }; }
const sessionResult = await restoreOAuthSession(identity.atp_did); if (!sessionResult.ok) { return { success: false, error: sessionResult.error }; } let credentialSession = sessionResult.value; let agent = new AtpBaseClient( credentialSession.fetchHandler.bind(credentialSession), ); let { data: existingSubscription } = await supabaseServerClient .from("publication_subscriptions") .select("*") .eq("identity", identity.atp_did) .eq("publication", publication) .single(); if (!existingSubscription) return { success: true };
// Delete from both collections (old and new schema) - one or both may exist let rkey = new AtUri(existingSubscription.uri).rkey; await Promise.all([ agent.pub.leaflet.graph.subscription .delete({ repo: credentialSession.did!, rkey }) .catch(() => {}), agent.site.standard.graph.subscription .delete({ repo: credentialSession.did!, rkey }) .catch(() => {}), ]);
await supabaseServerClient .from("publication_subscriptions") .delete() .eq("identity", identity.atp_did) .eq("publication", publication); revalidatePath("/lish/[did]/[publication]", "layout"); return { success: true };}