+23
-22
src/components/Login.tsx
+23
-22
src/components/Login.tsx
···
1
1
// src/components/Login.tsx
2
-
import { Agent } from "@atproto/api";
2
+
import AtpAgent, { Agent } from "@atproto/api";
3
3
import React, { useEffect, useRef, useState } from "react";
4
4
5
5
import { useAuth } from "~/providers/UnifiedAuthProvider";
6
+
import { useQueryIdentity, useQueryProfile } from "~/utils/useQuery";
6
7
7
8
// --- 1. The Main Component (Orchestrator with `compact` prop) ---
8
9
export default function Login({
···
110
111
// --- 3. Helper components for layouts, forms, and UI ---
111
112
112
113
// A new component to contain the logic for the compact dropdown
113
-
const CompactLoginButton = ({popup}:{popup?: boolean}) => {
114
+
const CompactLoginButton = ({ popup }: { popup?: boolean }) => {
114
115
const [showForm, setShowForm] = useState(false);
115
116
const formRef = useRef<HTMLDivElement>(null);
116
117
···
137
138
Log in
138
139
</button>
139
140
{showForm && (
140
-
<div className={`absolute ${popup ? `bottom-[calc(100%)]` :`top-full`} right-0 mt-2 w-80 bg-white dark:bg-gray-900 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 p-4 z-50`}>
141
+
<div
142
+
className={`absolute ${popup ? `bottom-[calc(100%)]` : `top-full`} right-0 mt-2 w-80 bg-white dark:bg-gray-900 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 p-4 z-50`}
143
+
>
141
144
<UnifiedLoginForm />
142
145
</div>
143
146
)}
···
268
271
269
272
// --- Profile Component (now supports a `large` prop for styling) ---
270
273
export const ProfileThing = ({
271
-
agent,
274
+
agent: _unused,
272
275
large = false,
273
276
}: {
274
-
agent: Agent | null;
277
+
agent?: Agent | null;
275
278
large?: boolean;
276
279
}) => {
277
-
const [profile, setProfile] = useState<any>(null);
280
+
const { agent } = useAuth();
281
+
const did = ((agent as AtpAgent).session?.did ?? (agent as AtpAgent)?.assertDid ?? agent?.did) as
282
+
| string
283
+
| undefined;
284
+
const { data: identity } = useQueryIdentity(did);
285
+
const { data: profiledata } = useQueryProfile(`at://${did}/app.bsky.actor.profile/self`);
286
+
const profile = profiledata?.value;
278
287
279
-
useEffect(() => {
280
-
const fetchUser = async () => {
281
-
const did = (agent as any)?.session?.did ?? (agent as any)?.assertDid;
282
-
if (!did) return;
283
-
try {
284
-
const res = await agent!.getProfile({ actor: did });
285
-
setProfile(res.data);
286
-
} catch (e) {
287
-
console.error("Failed to fetch profile", e);
288
-
}
289
-
};
290
-
if (agent) fetchUser();
291
-
}, [agent]);
288
+
function getAvatarUrl(p: typeof profile) {
289
+
const link = p?.avatar?.ref?.["$link"];
290
+
if (!link || !did) return null;
291
+
return `https://cdn.bsky.app/img/avatar/plain/${did}/${link}@jpeg`;
292
+
}
292
293
293
-
if (!profile) {
294
+
if (!profiledata) {
294
295
return (
295
296
// Skeleton loader
296
297
<div
···
316
317
className={`flex flex-row items-center gap-2.5 ${large ? "mb-1" : ""}`}
317
318
>
318
319
<img
319
-
src={profile?.avatar}
320
+
src={getAvatarUrl(profile) ?? undefined}
320
321
alt="avatar"
321
322
className={`object-cover rounded-full ${large ? "w-10 h-10" : "w-[30px] h-[30px]"}`}
322
323
/>
···
329
330
<div
330
331
className={` ${large ? "text-gray-500 dark:text-gray-400 text-sm" : "text-gray-500 dark:text-gray-400 text-xs"}`}
331
332
>
332
-
@{profile?.handle}
333
+
@{identity?.handle}
333
334
</div>
334
335
</div>
335
336
</div>