-
Notifications
You must be signed in to change notification settings - Fork 439
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add farcaster follower and relevant follower integration to
/profile
(
#1455) * chore: try adding farcaster integration * feat: refine ProfileHero * chore: put revalidate tag on fc profile * refactor: move logic to ProfileSocialCounters component * refactor: SocialCountTile * refactor: rename variables and add docs, fallback if referred is not defined * refactor: farcaster hooks * UI: remove divider on small screens * adjust referredUsers condition --------- Co-authored-by: valid <[email protected]>
- Loading branch information
1 parent
4bf7eba
commit e8174d2
Showing
4 changed files
with
167 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
src/app/(marketing)/profile/_components/ProfileSocialCounters.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import FarcasterImage from "@/../static/socialIcons/farcaster.svg" | ||
import { AvatarGroup } from "@/components/ui/AvatarGroup" | ||
import { Separator } from "@/components/ui/Separator" | ||
import { cn } from "@/lib/utils" | ||
import { PropsWithChildren } from "react" | ||
import { | ||
useFarcasterProfile, | ||
useRelevantFarcasterFollowers, | ||
} from "../_hooks/useFarcasterProfile" | ||
import { useProfile } from "../_hooks/useProfile" | ||
import { useReferredUsers } from "../_hooks/useReferredUsers" | ||
|
||
export const ProfileSocialCounters = ({ className }: any) => { | ||
const { data: referredUsers } = useReferredUsers() | ||
const { data: profile } = useProfile() | ||
const { farcasterProfile } = useFarcasterProfile(profile?.userId) | ||
const { relevantFollowers } = useRelevantFarcasterFollowers(farcasterProfile?.fid) | ||
|
||
return ( | ||
<div | ||
className={cn( | ||
"flex flex-wrap items-center justify-center gap-6 gap-y-4 sm:flex-nowrap", | ||
className | ||
)} | ||
> | ||
{referredUsers && ( | ||
<SocialCountTile count={referredUsers.length}>Guildmates</SocialCountTile> | ||
)} | ||
{farcasterProfile && ( | ||
<> | ||
<Separator orientation="vertical" className="h-10 md:h-12" /> | ||
<SocialCountTile count={farcasterProfile.following_count}> | ||
<FarcasterImage /> | ||
Following | ||
</SocialCountTile> | ||
|
||
<Separator orientation="vertical" className="h-10 max-sm:hidden md:h-12" /> | ||
{relevantFollowers?.length ? ( | ||
<RelevantFollowers | ||
relevantFollowers={relevantFollowers} | ||
followerCount={farcasterProfile.follower_count} | ||
/> | ||
) : ( | ||
<SocialCountTile count={farcasterProfile.follower_count}> | ||
<FarcasterImage /> | ||
Followers | ||
</SocialCountTile> | ||
)} | ||
</> | ||
)} | ||
</div> | ||
) | ||
} | ||
|
||
const SocialCountTile = ({ count, children }: PropsWithChildren<{ count: any }>) => ( | ||
<div className="flex flex-col items-center leading-tight"> | ||
<div className="font-bold md:text-lg">{count}</div> | ||
<div className="flex items-center gap-1 text-muted-foreground">{children}</div> | ||
</div> | ||
) | ||
|
||
const RelevantFollowers = ({ | ||
relevantFollowers, | ||
followerCount, | ||
}: { relevantFollowers: any; followerCount: number }) => { | ||
const [firstFc, secondFc] = relevantFollowers | ||
|
||
return ( | ||
<div className="flex items-center gap-2"> | ||
<AvatarGroup | ||
imageUrls={relevantFollowers.map(({ pfp_url }) => pfp_url)} | ||
count={followerCount} | ||
/> | ||
<div className="max-w-64 text-muted-foreground leading-tight"> | ||
Followed by <span className="font-bold">{firstFc.display_name}</span> | ||
{secondFc && ( | ||
<> | ||
<span>", "</span> | ||
<span className="font-bold">{secondFc.display_name}</span> | ||
</> | ||
)}{" "} | ||
and {followerCount - Math.min(2, relevantFollowers.length)} others on | ||
Farcaster | ||
</div> | ||
</div> | ||
) | ||
} |
35 changes: 35 additions & 0 deletions
35
src/app/(marketing)/profile/_hooks/useFarcasterProfile.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { FarcasterProfile } from "@guildxyz/types" | ||
import useUser from "components/[guild]/hooks/useUser" | ||
import useSWRImmutable from "swr/immutable" | ||
|
||
export const useFarcasterProfile = (guildUserId?: number) => { | ||
const linkedFcProfile = useSWRImmutable<FarcasterProfile[]>( | ||
guildUserId ? `/v2/users/${guildUserId}/farcaster-profiles` : null | ||
).data?.at(0) | ||
|
||
// API reference: https://docs.neynar.com/reference/user-bulk | ||
const { data, ...rest } = useSWRImmutable( | ||
linkedFcProfile | ||
? `https://api.neynar.com/v2/farcaster/user/bulk?api_key=NEYNAR_API_DOCS&fids=${linkedFcProfile.fid}` | ||
: null | ||
) | ||
return { farcasterProfile: data?.users.at(0), ...rest } | ||
} | ||
|
||
export const useRelevantFarcasterFollowers = (farcasterId?: number) => { | ||
const currentUser = useUser() | ||
const currentUserFcProfile = currentUser.farcasterProfiles?.at(0) | ||
|
||
// API reference: https://docs.neynar.com/reference/relevant-followers | ||
const { data, ...rest } = useSWRImmutable( | ||
farcasterId && currentUserFcProfile | ||
? `https://api.neynar.com/v2/farcaster/followers/relevant?api_key=NEYNAR_API_DOCS&target_fid=${farcasterId}&viewer_fid=${currentUserFcProfile.fid}` | ||
: null | ||
) | ||
return { | ||
relevantFollowers: data?.top_relevant_followers_hydrated?.map( | ||
({ user }) => user | ||
), | ||
...rest, | ||
} | ||
} |