tangled
alpha
login
or
join now
isuggest.selfce.st
/
strand
3
fork
atom
alternative tangled frontend (extremely wip)
3
fork
atom
overview
issues
pulls
pipelines
feat: basic authed navbar
serenity
3 weeks ago
7f0a9d3b
17e572f1
+38
-24
2 changed files
expand all
collapse all
unified
split
src
components
Nav
NavBarAuthed.tsx
lib
queries
get-avatar.ts
+31
-20
src/components/Nav/NavBarAuthed.tsx
···
1
1
import { UnderlineIconRouterLink } from "@/components/Animated/UnderlineIconRouterLink";
2
2
import { StrandIcon } from "@/components/Icons/Branding/StrandIcon";
3
3
+
import { Loading } from "@/components/Icons/Loading";
3
4
import { LucideLogIn } from "@/components/Icons/LucideLogIn";
5
5
+
import { useOAuth } from "@/lib/oauth";
6
6
+
import { useAvatarQuery } from "@/lib/queries/get-avatar";
7
7
+
import { err } from "@/lib/result";
4
8
5
9
export const NavBarAuthed = () => {
10
10
+
const oauth = useOAuth();
11
11
+
const { session } = oauth;
12
12
+
const did = session ? session.did : null;
13
13
+
const repoUrl = session ? new URL(session.server.issuer) : null;
14
14
+
const {
15
15
+
isLoading: isAvatarLoading,
16
16
+
data: avatarQueryData,
17
17
+
error: avatarQueryError,
18
18
+
} = useAvatarQuery({ did, repoUrl });
19
19
+
20
20
+
const avatarSrc = avatarQueryData === "#" ? undefined : avatarQueryData;
21
21
+
6
22
return (
7
23
<div className="bg-surface0 flex w-full items-center justify-between p-3">
8
8
-
<div className="flex items-center gap-1">
24
24
+
<div className="flex max-h-12 items-center gap-1">
9
25
<UnderlineIconRouterLink
10
26
to="/"
11
27
label="Strand"
···
16
32
className="text-lg font-semibold"
17
33
/>
18
34
</div>
19
19
-
<div className="flex items-center gap-1">
20
20
-
<UnderlineIconRouterLink
21
21
-
to="/login"
22
22
-
label="Sign In"
23
23
-
icon={LucideLogIn({})}
24
24
-
iconClassName="text-crust"
25
25
-
labelClassName="text-crust"
26
26
-
underlineClassName="bg-crust"
27
27
-
className="bg-accent rounded-sm p-1.5 pr-3 pl-3"
28
28
-
position="right"
29
29
-
iconTransitions={{ duration: 0.2, ease: "easeInOut" }}
30
30
-
iconVariants={{
31
31
-
hover: {
32
32
-
x: [0, 10, -10, 0],
33
33
-
opacity: [1, 0, 0, 1],
34
34
-
},
35
35
-
}}
36
36
-
/>
35
35
+
<div className="flex max-h-12 items-center gap-1">
36
36
+
<button className="cursor-pointer">
37
37
+
{isAvatarLoading ? (
38
38
+
<Loading />
39
39
+
) : avatarQueryError ? (
40
40
+
<p>{avatarQueryError.message}</p>
41
41
+
) : (
42
42
+
<img
43
43
+
src={avatarSrc}
44
44
+
className="border-accent h-10 rounded-full border transition-all hover:border-2"
45
45
+
/>
46
46
+
)}
47
47
+
</button>
37
48
</div>
38
49
</div>
39
50
);
40
40
-
}
51
51
+
};
+7
-4
src/lib/queries/get-avatar.ts
···
130
130
return ok(bskyProfile);
131
131
};
132
132
133
133
-
export const avatarQueryKey = (did: string) => ["avatar", did];
133
133
+
export const avatarQueryKey = (did: string | null) => ["avatar", did];
134
134
135
135
/**
136
136
* Hook that wraps useQuery with the query function being the getAvatar query. See below for the query function definition itself.
···
145
145
did,
146
146
repoUrl,
147
147
}: {
148
148
-
did: string;
149
149
-
repoUrl: URL;
148
148
+
did: string | null;
149
149
+
repoUrl: URL | null;
150
150
}) => {
151
151
return useQuery({
152
152
queryKey: avatarQueryKey(did),
153
153
-
queryFn: () => getAvatar({ did, repoUrl }),
153
153
+
queryFn: () => {
154
154
+
if (!did || !repoUrl) return "";
155
155
+
return getAvatar({ did, repoUrl });
156
156
+
},
154
157
});
155
158
};