[Playground] Feature: Social Page (#4519)
<img width="1143" alt="Screenshot 2024-09-10 at 2 30 34 PM" src="https://github.com/user-attachments/assets/c3848dce-0bb2-4bd7-ac8a-300f1757a207"> <!-- start pr-codex --> --- ## PR-Codex overview The focus of this PR is to add social profile functionality and API documentation to the app. ### Detailed summary - Added "Social" link to nav bar - Implemented `Badge` component with variants - Created `SocialProfiles` component to display user profiles - Added API documentation for social profiles > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { CodeExample } from "@/components/code/code-example";
|
||||
import { SocialProfiles } from "@/components/social/social-profiles";
|
||||
import ThirdwebProvider from "@/components/thirdweb-provider";
|
||||
import { metadataBase } from "@/lib/constants";
|
||||
import type { Metadata } from "next";
|
||||
import { APIHeader } from "../../../components/blocks/APIHeader";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase,
|
||||
title: "Social APIs | thirdweb Connect",
|
||||
description:
|
||||
"Retrieve any user's onchain identity from popular protocols like ENS, Lens, Farcaster, and more.",
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<ThirdwebProvider>
|
||||
<main className="pb-20 container px-0">
|
||||
<APIHeader
|
||||
title="Get any user's onchain identity"
|
||||
description={
|
||||
<>
|
||||
Gain context about your users and their profiles across other apps
|
||||
as soon as they sign into your app.
|
||||
</>
|
||||
}
|
||||
docsLink="https://portal.thirdweb.com/connect" // TODO: update this once we have Social API docs
|
||||
heroLink="/in-app-wallet.png"
|
||||
/>
|
||||
|
||||
<section className="space-y-8">
|
||||
<UserProfiles />
|
||||
</section>
|
||||
</main>
|
||||
</ThirdwebProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function UserProfiles() {
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-2xl sm:text-3xl font-semibold tracking-tight">
|
||||
Social Profiles
|
||||
</h2>
|
||||
<p className="max-w-[600px]">
|
||||
Get users' profiles across apps like ENS, Lens, Farcaster, and
|
||||
more.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<CodeExample
|
||||
preview={<SocialProfiles />}
|
||||
code={`import { useSocialProfiles, useActiveAccount } from "thirdweb/react";
|
||||
|
||||
function App(){
|
||||
const account = useActiveAccount();
|
||||
const { data: profiles } = useSocialProfiles({
|
||||
client,
|
||||
address: account?.address,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{profiles?.map((profile) => (
|
||||
<div className="flex gap-4 w-[300px] bg-background border rounded-lg p-4 shadow-md">
|
||||
{profile.avatar ? (
|
||||
<img
|
||||
src={resolveScheme({ client: THIRDWEB_CLIENT, uri: profile.avatar })}
|
||||
alt={profile.name}
|
||||
className="size-10 rounded-full"
|
||||
/>
|
||||
) : (
|
||||
<div className="size-10 rounded-full bg-muted-foreground" />
|
||||
)}
|
||||
<div className="flex gap-2 items-center justify-between flex-1">
|
||||
<div className="text-base font-semibold">{profile.name}</div>
|
||||
<Badge variant="secondary">{profile.type.toUpperCase()}</Badge>
|
||||
</div>
|
||||
</div >
|
||||
))
|
||||
}
|
||||
</div >);
|
||||
}; `}
|
||||
lang="tsx"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -41,6 +41,10 @@ export const navLinks: SidebarLink[] = [
|
||||
name: "In-App wallet",
|
||||
href: "/connect/in-app-wallet",
|
||||
},
|
||||
{
|
||||
name: "Social",
|
||||
href: "/connect/social",
|
||||
},
|
||||
{
|
||||
name: "Pay",
|
||||
href: "/connect/pay",
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
"use client";
|
||||
|
||||
import { THIRDWEB_CLIENT } from "@/lib/client";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
ConnectEmbed,
|
||||
type SocialProfile,
|
||||
useActiveAccount,
|
||||
useSocialProfiles,
|
||||
} from "thirdweb/react";
|
||||
import { resolveScheme } from "thirdweb/storage";
|
||||
import { WALLETS } from "../../lib/constants";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
export function SocialProfiles() {
|
||||
const [addressOverride, setAddressOverride] = useState<string | undefined>();
|
||||
const { theme } = useTheme();
|
||||
const account = useActiveAccount();
|
||||
const { data: profiles } = useSocialProfiles({
|
||||
client: THIRDWEB_CLIENT,
|
||||
address: addressOverride || account?.address,
|
||||
});
|
||||
|
||||
return account ? (
|
||||
<div className="flex flex-col gap-4">
|
||||
{profiles?.map((profile) => (
|
||||
<SocialProfileCard
|
||||
profile={profile}
|
||||
key={`${profile.type}-${profile.name}`}
|
||||
/>
|
||||
))}
|
||||
{profiles?.length === 0 && (
|
||||
<div>
|
||||
<div className="text-center text-lg font-semibold">
|
||||
No profiles found.
|
||||
</div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
setAddressOverride("0x2247d5d238d0f9d37184d8332aE0289d1aD9991b")
|
||||
}
|
||||
className="mt-6"
|
||||
>
|
||||
Preview another user
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<ConnectEmbed
|
||||
wallets={WALLETS}
|
||||
client={THIRDWEB_CLIENT}
|
||||
theme={theme === "light" ? "light" : "dark"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SocialProfileCard({ profile }: { profile: SocialProfile }) {
|
||||
return (
|
||||
<div className="flex gap-4 w-[300px] bg-background border rounded-lg p-4 shadow-md">
|
||||
{profile.avatar ? (
|
||||
<img
|
||||
src={resolveScheme({ client: THIRDWEB_CLIENT, uri: profile.avatar })}
|
||||
alt={profile.name}
|
||||
className="size-10 rounded-full"
|
||||
/>
|
||||
) : (
|
||||
<div className="size-10 rounded-full bg-muted-foreground" />
|
||||
)}
|
||||
<div className="flex gap-2 items-center justify-between flex-1">
|
||||
<div className="text-base font-semibold">{profile.name}</div>
|
||||
<Badge variant="secondary">{profile.type.toUpperCase()}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
import type * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants };
|
||||
Reference in New Issue
Block a user