From 7e9754f2b21d03f1fed9762a5537cc8088763f34 Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Thu, 30 Oct 2025 18:01:30 -0400 Subject: [PATCH] publish notifications again --- .../Interactions/Comments/commentAction.ts | 29 +++++++++---------- drizzle/relations.ts | 10 ++++++- drizzle/schema.ts | 15 +++++++++- supabase/database.types.ts | 29 +++++++++++++++++++ 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/app/lish/[did]/[publication]/[rkey]/Interactions/Comments/commentAction.ts b/app/lish/[did]/[publication]/[rkey]/Interactions/Comments/commentAction.ts index c0152464..3b88898d 100644 --- a/app/lish/[did]/[publication]/[rkey]/Interactions/Comments/commentAction.ts +++ b/app/lish/[did]/[publication]/[rkey]/Interactions/Comments/commentAction.ts @@ -8,6 +8,7 @@ import { TID } from "@atproto/common"; import { AtUri, lexToJson, Un$Typed } from "@atproto/api"; import { supabaseServerClient } from "supabase/serverClient"; import { Json } from "supabase/database.types"; +import { Notification } from "src/notifications"; export async function publishComment(args: { document: string; @@ -65,21 +66,19 @@ export async function publishComment(args: { } as unknown as Json, }) .select(); - // let notifications = [ - // { - // comment: uri.toString(), - // identity: new AtUri(args.document).host, - // reason: "reply-on-post", - // }, - // ]; - // if (args.comment.replyTo) - // notifications.push({ - // comment: uri.toString(), - // identity: new AtUri(args.comment.replyTo).host, - // reason: "reply-to-comment", - // }); - // // SOMEDAY: move this out the action with inngest or workflows - // await supabaseServerClient.from("notif_comments").insert(notifications); + let notifications: Notification[] = [ + { + recipient: new AtUri(args.document).host, + data: { type: "comment", comment_uri: uri.toString() }, + }, + ]; + if (args.comment.replyTo) + notifications.push({ + recipient: new AtUri(args.comment.replyTo).host, + data: { type: "comment", comment_uri: uri.toString() }, + }); + // SOMEDAY: move this out the action with inngest or workflows + await supabaseServerClient.from("notifications").insert(notifications); return { record: data?.[0].record as Json, diff --git a/drizzle/relations.ts b/drizzle/relations.ts index ee948385..f717cc4e 100644 --- a/drizzle/relations.ts +++ b/drizzle/relations.ts @@ -1,5 +1,5 @@ import { relations } from "drizzle-orm/relations"; -import { identities, publications, documents, comments_on_documents, bsky_profiles, entity_sets, entities, facts, email_auth_tokens, poll_votes_on_entity, permission_tokens, phone_rsvps_to_entity, custom_domains, custom_domain_routes, email_subscriptions_to_entity, atp_poll_records, atp_poll_votes, bsky_follows, subscribers_to_publications, permission_token_on_homepage, documents_in_publications, document_mentions_in_bsky, bsky_posts, publication_domains, leaflets_in_publications, publication_subscriptions, permission_token_rights } from "./schema"; +import { identities, publications, documents, comments_on_documents, bsky_profiles, entity_sets, entities, facts, email_auth_tokens, poll_votes_on_entity, permission_tokens, phone_rsvps_to_entity, custom_domains, custom_domain_routes, email_subscriptions_to_entity, atp_poll_records, atp_poll_votes, notifications, bsky_follows, subscribers_to_publications, permission_token_on_homepage, documents_in_publications, document_mentions_in_bsky, bsky_posts, publication_domains, leaflets_in_publications, publication_subscriptions, permission_token_rights } from "./schema"; export const publicationsRelations = relations(publications, ({one, many}) => ({ identity: one(identities, { @@ -27,6 +27,7 @@ export const identitiesRelations = relations(identities, ({one, many}) => ({ custom_domains_identity_id: many(custom_domains, { relationName: "custom_domains_identity_id_identities_id" }), + notifications: many(notifications), bsky_follows_follows: many(bsky_follows, { relationName: "bsky_follows_follows_identities_atp_did" }), @@ -193,6 +194,13 @@ export const atp_poll_recordsRelations = relations(atp_poll_records, ({many}) => atp_poll_votes: many(atp_poll_votes), })); +export const notificationsRelations = relations(notifications, ({one}) => ({ + identity: one(identities, { + fields: [notifications.recipient], + references: [identities.atp_did] + }), +})); + export const bsky_followsRelations = relations(bsky_follows, ({one}) => ({ identity_follows: one(identities, { fields: [bsky_follows.follows], diff --git a/drizzle/schema.ts b/drizzle/schema.ts index b270a464..4213ff2e 100644 --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -165,6 +165,13 @@ export const phone_rsvps_to_entity = pgTable("phone_rsvps_to_entity", { } }); +export const notification = pgTable("notification", { + recipient: text("recipient").primaryKey().notNull(), + created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), + read: boolean("read").default(false).notNull(), + data: jsonb("data").notNull(), +}); + export const custom_domain_routes = pgTable("custom_domain_routes", { id: uuid("id").defaultRandom().primaryKey().notNull(), domain: text("domain").notNull().references(() => custom_domains.domain), @@ -210,7 +217,6 @@ export const atp_poll_votes = pgTable("atp_poll_votes", { voter_did: text("voter_did").notNull(), poll_uri: text("poll_uri").notNull().references(() => atp_poll_records.uri, { onDelete: "cascade", onUpdate: "cascade" } ), poll_cid: text("poll_cid").notNull(), - option: text("option").notNull(), indexed_at: timestamp("indexed_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), }, (table) => { @@ -232,6 +238,13 @@ export const oauth_session_store = pgTable("oauth_session_store", { session: jsonb("session").notNull(), }); +export const notifications = pgTable("notifications", { + recipient: text("recipient").primaryKey().notNull().references(() => identities.atp_did, { onDelete: "cascade", onUpdate: "cascade" } ), + created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), + read: boolean("read").default(false).notNull(), + data: jsonb("data").notNull(), +}); + export const bsky_follows = pgTable("bsky_follows", { identity: text("identity").default('').notNull().references(() => identities.atp_did, { onDelete: "cascade" } ), follows: text("follows").notNull().references(() => identities.atp_did, { onDelete: "cascade" } ), diff --git a/supabase/database.types.ts b/supabase/database.types.ts index 164a9af0..bd919591 100644 --- a/supabase/database.types.ts +++ b/supabase/database.types.ts @@ -624,6 +624,35 @@ export type Database = { }, ] } + notifications: { + Row: { + created_at: string + data: Json + read: boolean + recipient: string + } + Insert: { + created_at?: string + data: Json + read?: boolean + recipient: string + } + Update: { + created_at?: string + data?: Json + read?: boolean + recipient?: string + } + Relationships: [ + { + foreignKeyName: "notifications_recipient_fkey" + columns: ["recipient"] + isOneToOne: true + referencedRelation: "identities" + referencedColumns: ["atp_did"] + }, + ] + } oauth_session_store: { Row: { key: string -- 2.51.2