diff --git a/app/(home-pages)/notifications/MentionNotification.tsx b/app/(home-pages)/notifications/MentionNotification.tsx index 58ee25ee..7b24349c 100644 --- a/app/(home-pages)/notifications/MentionNotification.tsx +++ b/app/(home-pages)/notifications/MentionNotification.tsx @@ -1,50 +1,39 @@ import { MentionTiny } from "components/Icons/MentionTiny"; import { ContentLayout, Notification } from "./Notification"; +import { HydratedQuoteNotification } from "src/notifications"; +import { PubLeafletDocument, PubLeafletPublication } from "lexicons/api"; +import { AtUri } from "@atproto/api"; +import { Avatar } from "components/Avatar"; -export const DummyPostMentionNotification = (props: {}) => { - return ( - } - actionText={<>celine mentioned your post} - content={ - - I'm just gonna put the description here. The surrounding context is - just sort of a pain to figure out -
-
Title of the Mentioned Post
-
- And here is the description that follows it -
-
-
- } - /> - ); -}; +export const QuoteNotification = (props: HydratedQuoteNotification) => { + const postView = props.bskyPost.post_view as any; + const author = postView.author; + const displayName = author.displayName || author.handle || "Someone"; + const docRecord = props.document.data as PubLeafletDocument.Record; + const pubRecord = props.document.documents_in_publications[0]?.publications + ?.record as PubLeafletPublication.Record; + const rkey = new AtUri(props.document.uri).rkey; + const postText = postView.record?.text || ""; -export const DummyUserMentionNotification = (props: { - cardBorderHidden: boolean; -}) => { return ( } - actionText={<>celine mentioned you} + actionText={<>{displayName} quoted your post} content={ - -
- ...llo this is the content of a post or whatever here it comes{" "} - @celine and here it - was! ooooh heck yeah the high is unre... + +
+ +
+              {postText}
+            
} diff --git a/app/(home-pages)/notifications/NotificationList.tsx b/app/(home-pages)/notifications/NotificationList.tsx index 9cee7bba..74f085a0 100644 --- a/app/(home-pages)/notifications/NotificationList.tsx +++ b/app/(home-pages)/notifications/NotificationList.tsx @@ -7,6 +7,7 @@ import { markAsRead } from "./getNotifications"; import { ReplyNotification } from "./ReplyNotification"; import { useIdentityData } from "components/IdentityProvider"; import { FollowNotification } from "./FollowNotification"; +import { QuoteNotification } from "./MentionNotification"; export function NotificationList({ notifications, @@ -42,6 +43,9 @@ export function NotificationList({ if (n.type === "subscribe") { return ; } + if (n.type === "quote") { + return ; + } })}
diff --git a/app/api/inngest/functions/index_post_mention.ts b/app/api/inngest/functions/index_post_mention.ts index 06150cb0..73edfbe2 100644 --- a/app/api/inngest/functions/index_post_mention.ts +++ b/app/api/inngest/functions/index_post_mention.ts @@ -3,6 +3,8 @@ import { inngest } from "../client"; import { AtpAgent, AtUri } from "@atproto/api"; import { Json } from "supabase/database.types"; import { ids } from "lexicons/api/lexicons"; +import { Notification, pingIdentityToUpdateNotification } from "src/notifications"; +import { v7 } from "uuid"; export const index_post_mention = inngest.createFunction( { id: "index_post_mention" }, @@ -37,6 +39,12 @@ export const index_post_mention = inngest.createFunction( return { message: `No post found for ${event.data.post_uri}` }; } + const documentUri = AtUri.make( + pub.identity_did, + ids.PubLeafletDocument, + path[0], + ).toString(); + await step.run("index-bsky-post", async () => { await supabaseServerClient.from("bsky_posts").insert({ uri: bsky_post.uri, @@ -45,13 +53,26 @@ export const index_post_mention = inngest.createFunction( }); await supabaseServerClient.from("document_mentions_in_bsky").insert({ uri: bsky_post.uri, - document: AtUri.make( - pub.identity_did, - ids.PubLeafletDocument, - path[0], - ).toString(), + document: documentUri, link: event.data.document_link, }); }); + + await step.run("create-notification", async () => { + // Only create notification if the quote is from someone other than the author + if (bsky_post.author.did !== pub.identity_did) { + const notification: Notification = { + id: v7(), + recipient: pub.identity_did, + data: { + type: "quote", + bsky_post_uri: bsky_post.uri, + document_uri: documentUri, + }, + }; + await supabaseServerClient.from("notifications").insert(notification); + await pingIdentityToUpdateNotification(pub.identity_did); + } + }); }, ); diff --git a/src/notifications.ts b/src/notifications.ts index 4b6301d6..cc6acab0 100644 --- a/src/notifications.ts +++ b/src/notifications.ts @@ -11,22 +11,25 @@ export type Notification = Omit, "data"> & { export type NotificationData = | { type: "comment"; comment_uri: string; parent_uri?: string } - | { type: "subscribe"; subscription_uri: string }; + | { type: "subscribe"; subscription_uri: string } + | { type: "quote"; bsky_post_uri: string; document_uri: string }; export type HydratedNotification = | HydratedCommentNotification - | HydratedSubscribeNotification; + | HydratedSubscribeNotification + | HydratedQuoteNotification; export async function hydrateNotifications( notifications: NotificationRow[], ): Promise> { // Call all hydrators in parallel - const [commentNotifications, subscribeNotifications] = await Promise.all([ + const [commentNotifications, subscribeNotifications, quoteNotifications] = await Promise.all([ hydrateCommentNotifications(notifications), hydrateSubscribeNotifications(notifications), + hydrateQuoteNotifications(notifications), ]); // Combine all hydrated notifications - const allHydrated = [...commentNotifications, ...subscribeNotifications]; + const allHydrated = [...commentNotifications, ...subscribeNotifications, ...quoteNotifications]; // Sort by created_at to maintain order allHydrated.sort( @@ -122,6 +125,46 @@ async function hydrateSubscribeNotifications(notifications: NotificationRow[]) { })); } +export type HydratedQuoteNotification = Awaited< + ReturnType +>[0]; + +async function hydrateQuoteNotifications(notifications: NotificationRow[]) { + const quoteNotifications = notifications.filter( + (n): n is NotificationRow & { data: ExtractNotificationType<"quote"> } => + (n.data as NotificationData)?.type === "quote", + ); + + if (quoteNotifications.length === 0) { + return []; + } + + // Fetch bsky post data and document data + const bskyPostUris = quoteNotifications.map((n) => n.data.bsky_post_uri); + const documentUris = quoteNotifications.map((n) => n.data.document_uri); + + const { data: bskyPosts } = await supabaseServerClient + .from("bsky_posts") + .select("*") + .in("uri", bskyPostUris); + + const { data: documents } = await supabaseServerClient + .from("documents") + .select("*, documents_in_publications(publications(*))") + .in("uri", documentUris); + + return quoteNotifications.map((notification) => ({ + id: notification.id, + recipient: notification.recipient, + created_at: notification.created_at, + type: "quote" as const, + bsky_post_uri: notification.data.bsky_post_uri, + document_uri: notification.data.document_uri, + bskyPost: bskyPosts?.find((p) => p.uri === notification.data.bsky_post_uri)!, + document: documents?.find((d) => d.uri === notification.data.document_uri)!, + })); +} + export async function pingIdentityToUpdateNotification(did: string) { let channel = supabaseServerClient.channel(`identity.atp_did:${did}`); await channel.send({