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: better layout
serenity
2 weeks ago
51d5617c
1f0b62d1
+103
-52
2 changed files
expand all
collapse all
unified
split
src
components
Profile
ProfileOverview.tsx
routes
_layout
$identifier
index.tsx
+53
src/components/Profile/ProfileOverview.tsx
···
1
1
+
import { Loading } from "@/components/Icons/Loading";
2
2
+
import { Avatar } from "@/components/Profile/Avatar";
3
3
+
import { useAvatarQuery } from "@/lib/queries/get-avatar";
4
4
+
import { useProfileQuery } from "@/lib/queries/get-profile";
5
5
+
import { useMiniDoc } from "@/lib/queries/resolve-minidoc";
6
6
+
7
7
+
export const ProfileOverview = ({ identifier }: { identifier: string }) => {
8
8
+
const {
9
9
+
isLoading: isMiniDocLoading,
10
10
+
error: miniDocQueryErr,
11
11
+
data: miniDocQueryData,
12
12
+
} = useMiniDoc(identifier);
13
13
+
const {
14
14
+
isLoading: isAvatarLoading,
15
15
+
error: avatarQueryErr,
16
16
+
data: avatarQueryData,
17
17
+
} = useAvatarQuery({
18
18
+
did: miniDocQueryData?.did ?? null,
19
19
+
repoUrl: miniDocQueryData ? new URL(miniDocQueryData.pds) : null,
20
20
+
});
21
21
+
const {
22
22
+
isLoading: isProfileLoading,
23
23
+
error: profileQueryErr,
24
24
+
data: profileQueryData,
25
25
+
} = useProfileQuery({
26
26
+
did: miniDocQueryData?.did ?? null,
27
27
+
repoUrl: miniDocQueryData ? new URL(miniDocQueryData.pds) : null,
28
28
+
});
29
29
+
30
30
+
const isLoading =
31
31
+
isMiniDocLoading ||
32
32
+
!miniDocQueryData ||
33
33
+
isAvatarLoading ||
34
34
+
!avatarQueryData ||
35
35
+
isProfileLoading ||
36
36
+
!profileQueryData;
37
37
+
const err = miniDocQueryErr ?? avatarQueryErr ?? profileQueryErr;
38
38
+
39
39
+
if (isLoading) return <Loading />;
40
40
+
if (err) return <p>{err.message}</p>;
41
41
+
42
42
+
const avatarUri = avatarQueryData;
43
43
+
44
44
+
return (
45
45
+
<div className="bg-surface0 w-fit">
46
46
+
{/* <p>{JSON.stringify(miniDocQueryData)}</p> */}
47
47
+
<Avatar
48
48
+
uri={avatarUri}
49
49
+
className="outline-overlay0 h-48 rounded-full outline"
50
50
+
/>
51
51
+
</div>
52
52
+
);
53
53
+
};
+50
-52
src/routes/_layout/$identifier/index.tsx
···
2
2
import { LucideBookOpen } from "lucide-react";
3
3
import type { ReactNode } from "react";
4
4
import { UnderlineIconRouterLink } from "@/components/Animated/UnderlineIconRouterLink";
5
5
-
import { Loading } from "@/components/Icons/Loading";
6
6
-
import { Avatar } from "@/components/Profile/Avatar";
7
7
-
import { useAvatarQuery } from "@/lib/queries/get-avatar";
8
8
-
import { useProfileQuery } from "@/lib/queries/get-profile";
9
9
-
import { useMiniDoc } from "@/lib/queries/resolve-minidoc";
5
5
+
import { z } from "zod/v4";
6
6
+
import { ProfileOverview } from "@/components/Profile/ProfileOverview";
7
7
+
8
8
+
const paramsSchema = z.object({
9
9
+
tab: z.string().optional(),
10
10
+
});
10
11
11
12
export const Route = createFileRoute("/_layout/$identifier/")({
12
13
component: RouteComponent,
14
14
+
validateSearch: paramsSchema,
13
15
});
14
16
15
17
function RouteComponent() {
16
18
const { identifier } = Route.useParams();
17
17
-
const {
18
18
-
isLoading: isMiniDocLoading,
19
19
-
error: miniDocQueryErr,
20
20
-
data: miniDocQueryData,
21
21
-
} = useMiniDoc(identifier);
22
22
-
const {
23
23
-
isLoading: isAvatarLoading,
24
24
-
error: avatarQueryErr,
25
25
-
data: avatarQueryData,
26
26
-
} = useAvatarQuery({
27
27
-
did: miniDocQueryData?.did ?? null,
28
28
-
repoUrl: miniDocQueryData ? new URL(miniDocQueryData.pds) : null,
29
29
-
});
30
30
-
const {
31
31
-
isLoading: isProfileLoading,
32
32
-
error: profileQueryErr,
33
33
-
data: profileQueryData,
34
34
-
} = useProfileQuery({
35
35
-
did: miniDocQueryData?.did ?? null,
36
36
-
repoUrl: miniDocQueryData ? new URL(miniDocQueryData.pds) : null,
37
37
-
});
19
19
+
const { tab } = Route.useSearch();
38
20
39
39
-
const isLoading =
40
40
-
isMiniDocLoading ||
41
41
-
!miniDocQueryData ||
42
42
-
isAvatarLoading ||
43
43
-
!avatarQueryData ||
44
44
-
isProfileLoading ||
45
45
-
!profileQueryData;
46
46
-
const err = miniDocQueryErr ?? avatarQueryErr ?? profileQueryErr;
21
21
+
const currTab = tab ?? "overview";
47
22
48
48
-
if (isLoading) return <Loading />;
49
49
-
if (err) return <p>{err.message}</p>;
50
50
-
51
51
-
const avatarUri = avatarQueryData;
52
52
-
53
53
-
const tabs: Array<{ to: string; icon: ReactNode; label: string }> = [
23
23
+
const tabs: Array<{
24
24
+
to: string;
25
25
+
icon: ReactNode;
26
26
+
label: string;
27
27
+
isCurrent: boolean;
28
28
+
}> = [
54
29
{
55
30
to: `/${identifier}`,
56
31
icon: <LucideBookOpen height={18} width={18} />,
57
32
label: "Overview",
33
33
+
isCurrent: currTab === "overview",
34
34
+
},
35
35
+
{
36
36
+
to: `/${identifier}`,
37
37
+
icon: <LucideBookOpen height={18} width={18} />,
38
38
+
label: "Overview",
39
39
+
isCurrent: currTab === "strings",
40
40
+
},
41
41
+
{
42
42
+
to: `/${identifier}`,
43
43
+
icon: <LucideBookOpen height={18} width={18} />,
44
44
+
label: "Overview",
45
45
+
isCurrent: currTab === "strings",
46
46
+
},
47
47
+
{
48
48
+
to: `/${identifier}`,
49
49
+
icon: <LucideBookOpen height={18} width={18} />,
50
50
+
label: "Overview",
51
51
+
isCurrent: currTab === "strings",
58
52
},
59
53
];
60
54
55
55
+
let tabComponent: ReactNode | undefined;
56
56
+
57
57
+
switch (currTab) {
58
58
+
case "overview":
59
59
+
tabComponent = <ProfileOverview identifier={identifier} />;
60
60
+
break;
61
61
+
default:
62
62
+
tabComponent = <></>;
63
63
+
}
64
64
+
61
65
return (
62
66
<div className="flex flex-col items-center">
63
63
-
<div className="bg-base flex w-screen pb-1 pl-4">
64
64
-
{tabs.map(({ to, icon, label }) => (
67
67
+
<div className="bg-base flex w-screen pl-4 gap-2">
68
68
+
{tabs.map(({ to, icon, label, isCurrent }) => (
65
69
<UnderlineIconRouterLink
66
70
to={to}
67
71
icon={icon}
68
72
label={label}
69
73
underlineClassName="bg-text"
70
70
-
labelClassName="text-text"
74
74
+
labelClassName={`text-text ${isCurrent ? "font-semibold" : ""}`}
71
75
iconClassName="text-text"
72
76
iconVariants={{}}
73
73
-
className="hover:bg-surface1 p-2 transition-all"
77
77
+
className={`hover:bg-surface1 rounded-t-lg p-2 px-4 transition-all ${isCurrent ? "bg-surface0" : ""}`}
74
78
/>
75
79
))}
76
80
</div>
77
77
-
<div className="border-overlay0 border-t w-full bg-surface0 min-h-screen">
78
78
-
<div className="bg-surface0 w-fit">
79
79
-
<p>{JSON.stringify(miniDocQueryData)}</p>
80
80
-
<Avatar
81
81
-
uri={avatarUri}
82
82
-
className="outline-overlay0 h-48 rounded-full outline"
83
83
-
/>
84
84
-
</div>
81
81
+
<div className="bg-surface0 min-h-screen w-full">
82
82
+
{tabComponent}
85
83
</div>
86
84
</div>
87
85
);