tangled
alpha
login
or
join now
whey.party
/
red-dwarf
82
fork
atom
an independent Bluesky client using Constellation, PDS Queries, and other services
reddwarf.app
frontend
spa
bluesky
reddwarf
microcosm
client
app
82
fork
atom
overview
issues
25
pulls
pipelines
constellation profilething
rimar1337
4 months ago
b279cb9c
06ea05da
+23
-22
1 changed file
expand all
collapse all
unified
split
src
components
Login.tsx
+23
-22
src/components/Login.tsx
···
1
1
// src/components/Login.tsx
2
2
-
import { Agent } from "@atproto/api";
2
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
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
113
-
const CompactLoginButton = ({popup}:{popup?: boolean}) => {
114
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
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
141
+
<div
142
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
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
271
-
agent,
274
274
+
agent: _unused,
272
275
large = false,
273
276
}: {
274
274
-
agent: Agent | null;
277
277
+
agent?: Agent | null;
275
278
large?: boolean;
276
279
}) => {
277
277
-
const [profile, setProfile] = useState<any>(null);
280
280
+
const { agent } = useAuth();
281
281
+
const did = ((agent as AtpAgent).session?.did ?? (agent as AtpAgent)?.assertDid ?? agent?.did) as
282
282
+
| string
283
283
+
| undefined;
284
284
+
const { data: identity } = useQueryIdentity(did);
285
285
+
const { data: profiledata } = useQueryProfile(`at://${did}/app.bsky.actor.profile/self`);
286
286
+
const profile = profiledata?.value;
278
287
279
279
-
useEffect(() => {
280
280
-
const fetchUser = async () => {
281
281
-
const did = (agent as any)?.session?.did ?? (agent as any)?.assertDid;
282
282
-
if (!did) return;
283
283
-
try {
284
284
-
const res = await agent!.getProfile({ actor: did });
285
285
-
setProfile(res.data);
286
286
-
} catch (e) {
287
287
-
console.error("Failed to fetch profile", e);
288
288
-
}
289
289
-
};
290
290
-
if (agent) fetchUser();
291
291
-
}, [agent]);
288
288
+
function getAvatarUrl(p: typeof profile) {
289
289
+
const link = p?.avatar?.ref?.["$link"];
290
290
+
if (!link || !did) return null;
291
291
+
return `https://cdn.bsky.app/img/avatar/plain/${did}/${link}@jpeg`;
292
292
+
}
292
293
293
293
-
if (!profile) {
294
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
319
-
src={profile?.avatar}
320
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
332
-
@{profile?.handle}
333
333
+
@{identity?.handle}
333
334
</div>
334
335
</div>
335
336
</div>