its for when you want to get like notifications for your reposts
1import { Did, Handle } from "@atcute/lexicons";
2import { Client, ok, simpleFetchHandler } from "@atcute/client";
3import type {} from "@atcute/bluesky";
4import type {} from "@atcute/atproto";
5import { isDid, parseCanonicalResourceUri } from "@atcute/lexicons/syntax";
6import { Component, createSignal, createEffect } from "solid-js";
7import { ATProtoActivity } from "./types.js";
8
9const profileCache = new Map<string, ActorData>();
10const handler = simpleFetchHandler({
11 service: "https://public.api.bsky.app",
12});
13const rpc = new Client({ handler });
14
15interface ActivityItemProps {
16 data: ATProtoActivity;
17}
18
19interface ActorData {
20 did: Did;
21 handle: Handle;
22 displayName?: string;
23}
24
25export const ActivityItem: Component<ActivityItemProps> = (props) => {
26 const [actorData, setActorData] = createSignal<ActorData | null>(null);
27 const [error, setError] = createSignal<string | null>(null);
28 const [postUrl, setPostUrl] = createSignal<string | null>(null);
29
30 const fetchProfile = async (did: string) => {
31 if (!isDid(did)) {
32 setError("user DID invalid");
33 return;
34 }
35 const resp = ok(
36 await rpc.get("app.bsky.actor.getProfile", {
37 params: { actor: did },
38 }),
39 );
40 const data: ActorData = {
41 did,
42 handle: resp.handle,
43 displayName: resp.displayName,
44 };
45 setActorData(data);
46 profileCache.set(data.did, data);
47 };
48
49 createEffect(() => {
50 const actorData = profileCache.get(props.data.did);
51 if (actorData) {
52 setActorData(actorData);
53 } else {
54 fetchProfile(props.data.did);
55 }
56
57 const postUri = parseCanonicalResourceUri(props.data.post_uri);
58 if (postUri.ok) {
59 setPostUrl(
60 `https://bsky.app/profile/${postUri.value.repo}/post/${postUri.value.rkey}`,
61 );
62 }
63 });
64
65 return (
66 <div
67 class={`flex flex-wrap items-center border py-1 px-2 ${
68 props.data.liked
69 ? "bg-green-50 border-green-200"
70 : "bg-red-50 border-red-200"
71 }`}
72 >
73 <p text-wrap>
74 <span text-lg>{props.data.liked ? "❤️" : "💔"}</span>{" "}
75 {(actorData() && (
76 <span font-medium text="sm gray-700">
77 {actorData()!.displayName ?? actorData()!.handle}{" "}
78 {actorData()!.displayName && (
79 <span font-normal text-gray-500>
80 (@{actorData()!.handle})
81 </span>
82 )}
83 </span>
84 )) ||
85 (error() && (
86 <span italic text="xs red-500">
87 !{error()}!
88 </span>
89 )) || (
90 <span font-medium text="sm gray-700">
91 {props.data.did}
92 </span>
93 )}{" "}
94 <span text-gray-800>{props.data.liked ? "liked" : "unliked"}</span>{" "}
95 <a
96 text-blue-800
97 hover="underline text-blue-400"
98 href={postUrl() ?? props.data.post_uri}
99 >
100 this post
101 </a>{" "}
102 </p>
103 <div grow />
104 <div text="xs gray-500 end">{new Date().toLocaleTimeString()}</div>
105 </div>
106 );
107};