tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
289
fork
atom
a tool for shared writing and social publishing
289
fork
atom
overview
issues
28
pulls
pipelines
render known followers
awarm.space
3 months ago
f8cd2462
07f3dc12
+45
-24
3 changed files
expand all
collapse all
unified
split
app
(home-pages)
p
[didOrHandle]
layout.tsx
api
rpc
[command]
get_profile_data.ts
components
ProfilePopover.tsx
+6
-15
app/(home-pages)/p/[didOrHandle]/layout.tsx
···
7
7
import { DashboardLayout } from "components/PageLayouts/DashboardLayout";
8
8
import { ProfileLayout } from "./ProfileLayout";
9
9
import { Agent } from "@atproto/api";
10
10
+
import { get_profile_data } from "app/api/rpc/[command]/get_profile_data";
10
11
11
12
export default async function ProfilePageLayout(props: {
12
13
params: Promise<{ didOrHandle: string }>;
···
33
34
}
34
35
did = resolved;
35
36
}
36
36
-
37
37
-
let agent = new Agent({
38
38
-
service: "https://public.api.bsky.app",
39
39
-
});
40
40
-
let profileReq = agent.app.bsky.actor.getProfile({ actor: did });
41
41
-
42
42
-
let publicationsReq = supabaseServerClient
43
43
-
.from("publications")
44
44
-
.select("*")
45
45
-
.eq("identity_did", did);
46
46
-
47
47
-
let [{ data: profile }, { data: publications }] = await Promise.all([
48
48
-
profileReq,
49
49
-
publicationsReq,
50
50
-
]);
37
37
+
let profileData = await get_profile_data.handler(
38
38
+
{ didOrHandle: did },
39
39
+
{ supabase: supabaseServerClient },
40
40
+
);
41
41
+
let { publications, profile } = profileData.result;
51
42
52
43
if (!profile) return null;
53
44
+22
-4
app/api/rpc/[command]/get_profile_data.ts
···
4
4
import { idResolver } from "app/(home-pages)/reader/idResolver";
5
5
import { supabaseServerClient } from "supabase/serverClient";
6
6
import { Agent } from "@atproto/api";
7
7
+
import { getIdentityData } from "actions/getIdentityData";
8
8
+
import { createOauthClient } from "src/atproto-oauth";
7
9
8
10
export type GetProfileDataReturnType = Awaited<
9
11
ReturnType<(typeof get_profile_data)["handler"]>
···
25
27
}
26
28
did = resolved;
27
29
}
30
30
+
let agent;
31
31
+
let authed_identity = await getIdentityData();
32
32
+
if (authed_identity?.atp_did) {
33
33
+
try {
34
34
+
const oauthClient = await createOauthClient();
35
35
+
let credentialSession = await oauthClient.restore(
36
36
+
authed_identity.atp_did,
37
37
+
);
38
38
+
agent = new Agent(credentialSession);
39
39
+
} catch (e) {
40
40
+
agent = new Agent({
41
41
+
service: "https://public.api.bsky.app",
42
42
+
});
43
43
+
}
44
44
+
} else {
45
45
+
agent = new Agent({
46
46
+
service: "https://public.api.bsky.app",
47
47
+
});
48
48
+
}
28
49
29
29
-
let agent = new Agent({
30
30
-
service: "https://public.api.bsky.app",
31
31
-
});
32
50
let profileReq = agent.app.bsky.actor.getProfile({ actor: did });
33
51
34
34
-
let publicationsReq = supabaseServerClient
52
52
+
let publicationsReq = supabase
35
53
.from("publications")
36
54
.select("*")
37
55
.eq("identity_did", did);
+17
-5
components/ProfilePopover.tsx
···
6
6
import { ProfileHeader } from "app/(home-pages)/p/[didOrHandle]/ProfileHeader";
7
7
import { SpeedyLink } from "./SpeedyLink";
8
8
import { Tooltip } from "./Tooltip";
9
9
+
import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs";
9
10
10
11
export const ProfilePopover = (props: {
11
12
trigger: React.ReactNode;
···
64
65
publications={data.publications}
65
66
popover
66
67
/>
67
67
-
<hr className="border-border" />
68
68
-
<div className="py-2 text-sm text-tertiary">
69
69
-
Followed by{" "}
70
70
-
<button className="hover:underline">celine and 4 others</button>
71
71
-
</div>
68
68
+
<KnownFollowers viewer={data.profile.viewer} />
72
69
</div>
73
70
) : (
74
71
<div className="text-secondary py-2 px-4">Profile not found</div>
···
76
73
</Tooltip>
77
74
);
78
75
};
76
76
+
77
77
+
let KnownFollowers = (props: { viewer: ProfileViewDetailed["viewer"] }) => {
78
78
+
if (!props.viewer?.knownFollowers) return null;
79
79
+
let count = props.viewer.knownFollowers.count;
80
80
+
return (
81
81
+
<>
82
82
+
<hr className="border-border" />
83
83
+
Followed by{" "}
84
84
+
<button className="hover:underline">
85
85
+
{props.viewer?.knownFollowers?.followers[0]?.displayName}{" "}
86
86
+
{count > 1 ? `and ${count - 1} other${count > 2 ? "s" : ""}` : ""}
87
87
+
</button>
88
88
+
</>
89
89
+
);
90
90
+
};