a tool for shared writing and social publishing

ping notifications and fix reply notifications

+42 -20
+5 -2
app/(home-pages)/notifications/NotificationList.tsx
··· 5 5 import { useEffect, createContext } from "react"; 6 6 import { markAsRead } from "./getNotifications"; 7 7 import { ReplyNotification } from "./ReplyNotification"; 8 + import { useIdentityData } from "components/IdentityProvider"; 8 9 9 10 export function NotificationList({ 10 11 notifications, ··· 13 14 notifications: HydratedNotification[]; 14 15 compact?: boolean; 15 16 }) { 17 + let { mutate } = useIdentityData(); 16 18 useEffect(() => { 17 - setTimeout(() => { 18 - markAsRead(); 19 + setTimeout(async () => { 20 + await markAsRead(); 21 + mutate(); 19 22 }, 500); 20 23 }, []); 21 24
+13 -17
app/lish/[did]/[publication]/[rkey]/Interactions/Comments/commentAction.ts
··· 8 8 import { AtUri, lexToJson, Un$Typed } from "@atproto/api"; 9 9 import { supabaseServerClient } from "supabase/serverClient"; 10 10 import { Json } from "supabase/database.types"; 11 - import { Notification } from "src/notifications"; 11 + import { 12 + Notification, 13 + pingIdentityToUpdateNotification, 14 + } from "src/notifications"; 12 15 import { v7 } from "uuid"; 13 16 14 17 export async function publishComment(args: { ··· 68 71 }) 69 72 .select(); 70 73 let notifications: Notification[] = []; 71 - if ( 72 - !args.comment.replyTo && 73 - new AtUri(args.document).host !== credentialSession.did 74 - ) 74 + let recipient = args.comment.replyTo 75 + ? new AtUri(args.comment.replyTo).host 76 + : new AtUri(args.document).host; 77 + if (recipient !== credentialSession.did) { 75 78 notifications.push({ 76 79 id: v7(), 77 - recipient: new AtUri(args.document).host, 78 - data: { type: "comment", comment_uri: uri.toString() }, 79 - }); 80 - if ( 81 - args.comment.replyTo && 82 - new AtUri(args.comment.replyTo).host !== credentialSession.did 83 - ) 84 - notifications.push({ 85 - id: v7(), 86 - recipient: new AtUri(args.comment.replyTo).host, 80 + recipient, 87 81 data: { 88 82 type: "comment", 89 83 comment_uri: uri.toString(), 90 84 parent_uri: args.comment.replyTo, 91 85 }, 92 86 }); 93 - // SOMEDAY: move this out the action with inngest or workflows 94 - await supabaseServerClient.from("notifications").insert(notifications); 87 + // SOMEDAY: move this out the action with inngest or workflows 88 + await supabaseServerClient.from("notifications").insert(notifications); 89 + await pingIdentityToUpdateNotification(recipient); 90 + } 95 91 96 92 return { 97 93 record: data?.[0].record as Json,
+14 -1
components/IdentityProvider.tsx
··· 1 1 "use client"; 2 2 import { getIdentityData } from "actions/getIdentityData"; 3 - import { createContext, useContext } from "react"; 3 + import { createContext, useContext, useEffect } from "react"; 4 4 import useSWR, { KeyedMutator, mutate } from "swr"; 5 5 import { DashboardState } from "./PageLayouts/DashboardLayout"; 6 + import { supabaseBrowserClient } from "supabase/browserClient"; 6 7 7 8 export type InterfaceState = { 8 9 dashboards: { [id: string]: DashboardState | undefined }; ··· 20 21 let { data: identity, mutate } = useSWR("identity", () => getIdentityData(), { 21 22 fallbackData: props.initialValue, 22 23 }); 24 + useEffect(() => { 25 + if (!identity?.atp_did) return; 26 + let supabase = supabaseBrowserClient(); 27 + let channel = supabase.channel(`identity.atp_did:${identity.atp_did}`); 28 + channel.on("broadcast", { event: "notification" }, () => { 29 + mutate(); 30 + }); 31 + channel.subscribe(); 32 + return () => { 33 + channel.unsubscribe(); 34 + }; 35 + }, [identity?.atp_did]); 23 36 return ( 24 37 <IdentityContext.Provider value={{ identity, mutate }}> 25 38 {props.children}
+10
src/notifications.ts
··· 127 127 ), 128 128 })); 129 129 } 130 + 131 + export async function pingIdentityToUpdateNotification(did: string) { 132 + let channel = supabaseServerClient.channel(`identity.atp_did:${did}`); 133 + await channel.send({ 134 + type: "broadcast", 135 + event: "notification", 136 + payload: { message: "poke" }, 137 + }); 138 + await supabaseServerClient.removeChannel(channel); 139 + }