tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
289
fork
atom
a tool for shared writing and social publishing
289
fork
atom
overview
issues
28
pulls
pipelines
ping notifications and fix reply notifications
awarm.space
4 months ago
32ddb793
1ee32442
+42
-20
4 changed files
expand all
collapse all
unified
split
app
(home-pages)
notifications
NotificationList.tsx
lish
[did]
[publication]
[rkey]
Interactions
Comments
commentAction.ts
components
IdentityProvider.tsx
src
notifications.ts
+5
-2
app/(home-pages)/notifications/NotificationList.tsx
···
5
import { useEffect, createContext } from "react";
6
import { markAsRead } from "./getNotifications";
7
import { ReplyNotification } from "./ReplyNotification";
0
8
9
export function NotificationList({
10
notifications,
···
13
notifications: HydratedNotification[];
14
compact?: boolean;
15
}) {
0
16
useEffect(() => {
17
-
setTimeout(() => {
18
-
markAsRead();
0
19
}, 500);
20
}, []);
21
···
5
import { useEffect, createContext } from "react";
6
import { markAsRead } from "./getNotifications";
7
import { ReplyNotification } from "./ReplyNotification";
8
+
import { useIdentityData } from "components/IdentityProvider";
9
10
export function NotificationList({
11
notifications,
···
14
notifications: HydratedNotification[];
15
compact?: boolean;
16
}) {
17
+
let { mutate } = useIdentityData();
18
useEffect(() => {
19
+
setTimeout(async () => {
20
+
await markAsRead();
21
+
mutate();
22
}, 500);
23
}, []);
24
+13
-17
app/lish/[did]/[publication]/[rkey]/Interactions/Comments/commentAction.ts
···
8
import { AtUri, lexToJson, Un$Typed } from "@atproto/api";
9
import { supabaseServerClient } from "supabase/serverClient";
10
import { Json } from "supabase/database.types";
11
-
import { Notification } from "src/notifications";
0
0
0
12
import { v7 } from "uuid";
13
14
export async function publishComment(args: {
···
68
})
69
.select();
70
let notifications: Notification[] = [];
71
-
if (
72
-
!args.comment.replyTo &&
73
-
new AtUri(args.document).host !== credentialSession.did
74
-
)
75
notifications.push({
76
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,
87
data: {
88
type: "comment",
89
comment_uri: uri.toString(),
90
parent_uri: args.comment.replyTo,
91
},
92
});
93
-
// SOMEDAY: move this out the action with inngest or workflows
94
-
await supabaseServerClient.from("notifications").insert(notifications);
0
0
95
96
return {
97
record: data?.[0].record as Json,
···
8
import { AtUri, lexToJson, Un$Typed } from "@atproto/api";
9
import { supabaseServerClient } from "supabase/serverClient";
10
import { Json } from "supabase/database.types";
11
+
import {
12
+
Notification,
13
+
pingIdentityToUpdateNotification,
14
+
} from "src/notifications";
15
import { v7 } from "uuid";
16
17
export async function publishComment(args: {
···
71
})
72
.select();
73
let notifications: Notification[] = [];
74
+
let recipient = args.comment.replyTo
75
+
? new AtUri(args.comment.replyTo).host
76
+
: new AtUri(args.document).host;
77
+
if (recipient !== credentialSession.did) {
78
notifications.push({
79
id: v7(),
80
+
recipient,
0
0
0
0
0
0
0
0
0
81
data: {
82
type: "comment",
83
comment_uri: uri.toString(),
84
parent_uri: args.comment.replyTo,
85
},
86
});
87
+
// SOMEDAY: move this out the action with inngest or workflows
88
+
await supabaseServerClient.from("notifications").insert(notifications);
89
+
await pingIdentityToUpdateNotification(recipient);
90
+
}
91
92
return {
93
record: data?.[0].record as Json,
+14
-1
components/IdentityProvider.tsx
···
1
"use client";
2
import { getIdentityData } from "actions/getIdentityData";
3
-
import { createContext, useContext } from "react";
4
import useSWR, { KeyedMutator, mutate } from "swr";
5
import { DashboardState } from "./PageLayouts/DashboardLayout";
0
6
7
export type InterfaceState = {
8
dashboards: { [id: string]: DashboardState | undefined };
···
20
let { data: identity, mutate } = useSWR("identity", () => getIdentityData(), {
21
fallbackData: props.initialValue,
22
});
0
0
0
0
0
0
0
0
0
0
0
0
23
return (
24
<IdentityContext.Provider value={{ identity, mutate }}>
25
{props.children}
···
1
"use client";
2
import { getIdentityData } from "actions/getIdentityData";
3
+
import { createContext, useContext, useEffect } from "react";
4
import useSWR, { KeyedMutator, mutate } from "swr";
5
import { DashboardState } from "./PageLayouts/DashboardLayout";
6
+
import { supabaseBrowserClient } from "supabase/browserClient";
7
8
export type InterfaceState = {
9
dashboards: { [id: string]: DashboardState | undefined };
···
21
let { data: identity, mutate } = useSWR("identity", () => getIdentityData(), {
22
fallbackData: props.initialValue,
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]);
36
return (
37
<IdentityContext.Provider value={{ identity, mutate }}>
38
{props.children}
+10
src/notifications.ts
···
127
),
128
}));
129
}
0
0
0
0
0
0
0
0
0
0
···
127
),
128
}));
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
+
}