diff --git a/app/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx b/app/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx
index 50e5c21e..ce5c5cd6 100644
--- a/app/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx
+++ b/app/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx
@@ -46,6 +46,7 @@ export const ProfileHeader = (props: {
{/*
*/}
{props.publications.map((p) => (
diff --git a/app/api/rpc/[command]/get_profile_data.ts b/app/api/rpc/[command]/get_profile_data.ts
new file mode 100644
index 00000000..1e3dceb6
--- /dev/null
+++ b/app/api/rpc/[command]/get_profile_data.ts
@@ -0,0 +1,61 @@
+import { z } from "zod";
+import { makeRoute } from "../lib";
+import type { Env } from "./route";
+import { idResolver } from "app/(home-pages)/reader/idResolver";
+
+export type GetProfileDataReturnType = Awaited<
+ ReturnType<(typeof get_profile_data)["handler"]>
+>;
+
+export const get_profile_data = makeRoute({
+ route: "get_profile_data",
+ input: z.object({
+ didOrHandle: z.string(),
+ }),
+ handler: async ({ didOrHandle }, { supabase }: Pick) => {
+ // Resolve handle to DID if necessary
+ let did = didOrHandle;
+
+ if (!didOrHandle.startsWith("did:")) {
+ const resolved = await idResolver.handle.resolve(didOrHandle);
+ if (!resolved) {
+ throw new Error("Could not resolve handle to DID");
+ }
+ did = resolved;
+ }
+
+ // Fetch profile
+ const { data: profile, error: profileError } = await supabase
+ .from("bsky_profiles")
+ .select("*")
+ .eq("did", did)
+ .single();
+
+ if (profileError) {
+ throw new Error(`Failed to fetch profile: ${profileError.message}`);
+ }
+
+ if (!profile) {
+ throw new Error("Profile not found");
+ }
+
+ // Fetch publications for the DID
+ const { data: publications, error: publicationsError } = await supabase
+ .from("publications")
+ .select("*")
+ .eq("identity_did", did);
+
+ if (publicationsError) {
+ throw new Error(
+ `Failed to fetch publications: ${publicationsError.message}`,
+ );
+ }
+
+ return {
+ result: {
+ profile,
+ publications: publications || [],
+ },
+ };
+ },
+});
diff --git a/app/api/rpc/[command]/route.ts b/app/api/rpc/[command]/route.ts
index 4767ffb4..df7a865b 100644
--- a/app/api/rpc/[command]/route.ts
+++ b/app/api/rpc/[command]/route.ts
@@ -13,6 +13,7 @@ import { get_leaflet_data } from "./get_leaflet_data";
import { get_publication_data } from "./get_publication_data";
import { search_publication_names } from "./search_publication_names";
import { search_publication_documents } from "./search_publication_documents";
+import { get_profile_data } from "./get_profile_data";
let supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_API_URL as string,
@@ -39,6 +40,7 @@ let Routes = [
get_publication_data,
search_publication_names,
search_publication_documents,
+ get_profile_data,
];
export async function POST(
req: Request,
diff --git a/app/lish/[did]/[publication]/[rkey]/Interactions/Comments/index.tsx b/app/lish/[did]/[publication]/[rkey]/Interactions/Comments/index.tsx
index d82aeea4..b848aa3f 100644
--- a/app/lish/[did]/[publication]/[rkey]/Interactions/Comments/index.tsx
+++ b/app/lish/[did]/[publication]/[rkey]/Interactions/Comments/index.tsx
@@ -18,11 +18,12 @@ import { usePathname } from "next/navigation";
import { QuoteContent } from "../Quotes";
import { timeAgo } from "src/utils/timeAgo";
import { useLocalizedDate } from "src/hooks/useLocalizedDate";
+import { ProfilePopover } from "components/ProfilePopover";
export type Comment = {
record: Json;
uri: string;
- bsky_profiles: { record: Json } | null;
+ bsky_profiles: { record: Json; did: string } | null;
};
export function Comments(props: {
document_uri: string;
@@ -109,17 +110,25 @@ const Comment = (props: {
document: string;
comment: Comment;
comments: Comment[];
- profile?: AppBskyActorProfile.Record;
+ profile: AppBskyActorProfile.Record;
record: PubLeafletComment.Record;
pageId?: string;
}) => {
+ const did = props.comment.bsky_profiles?.did;
+
return (
{props.record.attachment &&
PubLeafletComment.isLinearDocumentQuote(props.record.attachment) && (
@@ -291,96 +300,3 @@ const DatePopover = (props: { date: string }) => {
);
};
-
-const ProfilePopover = (props: {
- profile: AppBskyActorProfile.Record;
- comment: string;
-}) => {
- let commenterId = new AtUri(props.comment).host;
-
- return (
- <>
-
- {props.profile.displayName}
-
- {/*
- {
- setHovering(true);
- hoverTimeout.current = window.setTimeout(() => {
- setLoadProfile(true);
- }, 500);
- }}
- onMouseOut={() => {
- setHovering(false);
- clearTimeout(hoverTimeout.current);
- }}
- className="font-bold text-tertiary text-sm hover:underline"
- >
- {props.profile.displayName}
-
- }
- className="max-w-sm"
- >
- {profile && (
- <>
-
-
-
-
-
-
- {profile.description}
-
-
- {!profile.viewer?.following ? (
-
- Following
-
- ) : (
-
- Follow
-
- )}
- {profile.viewer?.followedBy && (
-
Follows You
- )}
-
-
-
-
-
-
-
- {profile.viewer?.knownFollowers?.followers.map((follower) => {
- return (
-
- );
- })}
-
-
-
-
- >
- )}
-
- */}
- >
- );
-};
diff --git a/components/ProfilePopover.tsx b/components/ProfilePopover.tsx
new file mode 100644
index 00000000..1a890cc5
--- /dev/null
+++ b/components/ProfilePopover.tsx
@@ -0,0 +1,42 @@
+"use client";
+import { ProfileHeader } from "app/p/[didOrHandle]/(profile)/ProfileHeader";
+import { Popover } from "./Popover";
+import useSWR from "swr";
+import { callRPC } from "app/api/rpc/client";
+import { useState } from "react";
+
+export const ProfilePopover = (props: {
+ trigger: React.ReactNode;
+ didOrHandle: string;
+}) => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ const { data, isLoading } = useSWR(
+ isOpen ? ["profile-data", props.didOrHandle] : null,
+ async () => {
+ const response = await callRPC("get_profile_data", {
+ didOrHandle: props.didOrHandle,
+ });
+ return response.result;
+ },
+ );
+
+ return (
+
+ {isLoading ? (
+ Loading...
+ ) : data ? (
+
+ ) : (
+ Profile not found
+ )}
+
+ );
+};