Skip to content

Commit

Permalink
wallet social profiles in dashboard (#4502)
Browse files Browse the repository at this point in the history
before:
<img width="1942" alt="image" src="https://github.com/user-attachments/assets/8ac59faa-f81c-4670-98e5-f750ce68e5d6">

after:
<img width="1819" alt="image" src="https://github.com/user-attachments/assets/c8c015eb-528b-44a7-8cd5-478ca3ca907e">

<!-- start pr-codex -->

---

## PR-Codex overview
This PR adds social API domain support, new UI components, and updates dependencies.

### Detailed summary
- Added `THIRDWEB_SOCIAL_API_DOMAIN` in `urls.ts`
- Added `WalletAddress` component
- Updated UI components in various files
- Added new dependencies like `@radix-ui/react-avatar` and `@radix-ui/react-hover-card`

> The following files were skipped due to too many changes: `apps/dashboard/src/components/wallets/ConnectWalletMiniPlayground/MiniPlayground.tsx`, `apps/dashboard/src/contract-ui/tabs/analytics/page.tsx`, `apps/dashboard/src/components/contract-components/contract-deploy-form/split-fieldset.tsx`, `apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx`, `apps/dashboard/src/contract-ui/tabs/manage/components/InstalledModulesTable.tsx`, `apps/dashboard/src/components/contract-components/contract-table-v2/index.tsx`, `apps/dashboard/src/components/pay/PayAnalytics/components/PaymentHistory.tsx`, `apps/dashboard/src/contract-ui/tabs/overview/components/MarketplaceDetails.tsx`, `apps/dashboard/src/contract-ui/tabs/nfts/components/table.tsx`, `apps/dashboard/src/pages/[chain_id]/[...paths].tsx`, `apps/dashboard/src/components/embedded-wallets/Users/index.tsx`, `apps/dashboard/src/components/engine/overview/engine-overview.tsx`, `apps/dashboard/src/components/engine/permissions/access-tokens-table.tsx`, `apps/dashboard/src/contract-ui/tabs/shared-components/marketplace-table.tsx`, `apps/dashboard/src/pages/dashboard/connect/analytics.tsx`, `apps/dashboard/src/app/(dashboard)/styleguide/page.tsx`, `apps/dashboard/src/contract-ui/tabs/account-permissions/components/account-signer.tsx`, `apps/dashboard/src/components/engine/engine-instances-table.tsx`, `apps/dashboard/src/components/engine/overview/backend-wallets-table.tsx`, `apps/dashboard/src/components/engine/overview/transactions-table.tsx`, `apps/dashboard/src/components/pay/PayAnalytics/components/PayCustomersTable.tsx`, `apps/dashboard/src/@/components/ui/hover-card.tsx`, `apps/dashboard/src/@/components/ui/avatar.tsx`, `apps/dashboard/src/@/components/blocks/wallet-address.tsx`, `pnpm-lock.yaml`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
  • Loading branch information
jnsdls committed Sep 10, 2024
1 parent 8636c28 commit 9aa5b62
Show file tree
Hide file tree
Showing 52 changed files with 994 additions and 723 deletions.
2 changes: 2 additions & 0 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@
"@n8tb1t/use-scroll-position": "^2.0.3",
"@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-aspect-ratio": "^1.0.3",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-dialog": "1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-hover-card": "^1.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-progress": "^1.1.0",
Expand Down
190 changes: 190 additions & 0 deletions apps/dashboard/src/@/components/blocks/wallet-address.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
"use client";

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card";
import { Check, Copy, ExternalLinkIcon } from "lucide-react";
import { useMemo, useState } from "react";
import {
Blobbie,
MediaRenderer,
type SocialProfile,
useSocialProfiles,
} from "thirdweb/react";
import { resolveScheme } from "thirdweb/storage";
import { thirdwebClient } from "../../constants/client";
import { cn } from "../../lib/utils";
import { Badge } from "../ui/badge";
import { Button } from "../ui/button";

export function WalletAddress(props: {
address: string;
shortenAddress?: boolean;
className?: string;
}) {
const [shortenedAddress, lessShortenedAddress] = useMemo(() => {
return [
props.shortenAddress !== false
? `${props.address.slice(0, 6)}...${props.address.slice(-4)}`
: props.address,
`${props.address.slice(0, 14)}...${props.address.slice(-12)}`,
];
}, [props.address, props.shortenAddress]);

const profiles = useSocialProfiles({
address: props.address,
client: thirdwebClient,
});

const [isCopied, setIsCopied] = useState(false);

const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText(props.address);
setIsCopied(true);
setTimeout(() => setIsCopied(false), 2000); // Reset after 2 seconds
} catch (err) {
console.error("Failed to copy: ", err);
}
};

return (
<HoverCard>
<HoverCardTrigger asChild>
<Button
onClick={(e) => e.stopPropagation()}
variant="link"
className={cn(
"flex flex-row gap-2 items-center px-0",
props.className,
)}
>
<WalletAvatar
address={props.address}
profiles={profiles.data || []}
/>
<span className="font-mono cursor-pointer">
{profiles.data?.[0]?.name || shortenedAddress}
</span>
</Button>
</HoverCardTrigger>
<HoverCardContent
className="w-80 border-border"
onClick={(e) => {
// do not close the hover card when clicking anywhere in the content
e.stopPropagation();
}}
>
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold">Wallet Address</h3>
<Button
variant="outline"
size="sm"
onClick={copyToClipboard}
className="flex items-center gap-2"
>
{isCopied ? (
<Check className="h-4 w-4" />
) : (
<Copy className="h-4 w-4" />
)}
{isCopied ? "Copied!" : "Copy"}
</Button>
</div>
<p className="text-sm font-mono bg-muted p-2 rounded text-center">
{lessShortenedAddress}
</p>
<h3 className="text-lg font-semibold">Social Profiles</h3>
{profiles.isLoading ? (
<p className="text-sm text-muted-foreground">Loading profiles...</p>
) : !profiles.data?.length ? (
<p className="text-sm text-muted-foreground">No profiles found</p>
) : (
profiles.data?.map((profile) => (
<div
className="flex flex-row gap-2 items-center"
key={profile.type + profile.name}
>
{profile.avatar &&
(profile.avatar.startsWith("http") ||
profile.avatar?.startsWith("ipfs")) && (
<Avatar>
<AvatarImage
src={resolveScheme({
client: thirdwebClient,
uri: profile.avatar,
})}
alt={profile.name}
/>
{profile.name && (
<AvatarFallback>
{profile.name.slice(0, 2)}
</AvatarFallback>
)}
</Avatar>
)}
<div className="flex flex-col gap-1 w-full">
<div className="flex flex-row gap-4 w-full items-center justify-between">
<h4 className="text-md font-semibold">{profile.name}</h4>
<Badge variant="outline">{profile.type}</Badge>
</div>
{profile.bio && (
<p className="text-sm text-muted-foreground whitespace-normal line-clamp-1">
{profile.bio}
</p>
)}
</div>
</div>
))
)}
<Button
asChild
variant="upsell"
className="text-sm flex flex-row gap-2 items-center"
size="sm"
>
<a
target="_blank"
href="https://blog.thirdweb.com/changelog/introducing-the-social-sdk?ref=dashboard-social-wallet"
rel="noreferrer"
>
Learn more
<ExternalLinkIcon className="size-4" />
</a>
</Button>
</div>
</HoverCardContent>
</HoverCard>
);
}

function WalletAvatar(props: {
address: string;
profiles: SocialProfile[];
}) {
const avatar = useMemo(() => {
return props.profiles.find(
(profile) =>
profile.avatar &&
(profile.avatar.startsWith("http") ||
profile.avatar.startsWith("ipfs")),
)?.avatar;
}, [props.profiles]);
return (
<div className="size-6 overflow-hidden rounded-full">
{avatar ? (
<MediaRenderer
client={thirdwebClient}
src={avatar}
className="size-6"
/>
) : (
<Blobbie address={props.address} size={24} />
)}
</div>
);
}
50 changes: 50 additions & 0 deletions apps/dashboard/src/@/components/ui/avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use client";

import * as AvatarPrimitive from "@radix-ui/react-avatar";
import * as React from "react";

import { cn } from "@/lib/utils";

const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className,
)}
{...props}
/>
));
Avatar.displayName = AvatarPrimitive.Root.displayName;

const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
));
AvatarImage.displayName = AvatarPrimitive.Image.displayName;

const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className,
)}
{...props}
/>
));
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;

export { Avatar, AvatarImage, AvatarFallback };
2 changes: 2 additions & 0 deletions apps/dashboard/src/@/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const buttonVariants = cva(
"bg-secondary hover:bg-secondary/80 text-semibold text-secondary-foreground ",
ghost: "hover:bg-accent text-semibold hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline text-semibold",
upsell:
"bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200",
},
size: {
default: "h-10 px-4 py-2",
Expand Down
29 changes: 29 additions & 0 deletions apps/dashboard/src/@/components/ui/hover-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
import * as React from "react";

import { cn } from "@/lib/utils";

const HoverCard = HoverCardPrimitive.Root;

const HoverCardTrigger = HoverCardPrimitive.Trigger;

const HoverCardContent = React.forwardRef<
React.ElementRef<typeof HoverCardPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<HoverCardPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
));
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;

export { HoverCard, HoverCardTrigger, HoverCardContent };
16 changes: 6 additions & 10 deletions apps/dashboard/src/app/(dashboard)/styleguide/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { cn } from "@/lib/utils";
import { CircleAlertIcon } from "lucide-react";
import {
Alert,
AlertDescription,
AlertTitle,
} from "../../../@/components/ui/alert";
import { Badge } from "../../../@/components/ui/badge";
import { Button } from "../../../@/components/ui/button";
import { Checkbox } from "../../../@/components/ui/checkbox";
import { Input } from "../../../@/components/ui/input";
import { Label } from "../../../@/components/ui/label";

export const metadata = {
robots: "noindex, nofollow",
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/app/(dashboard)/trending/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Sidebar } from "@/components/blocks/Sidebar";
import { type SortBy, type TimeRange, fetchTopContracts } from "lib/search";
import type { Metadata } from "next";
import { Sidebar } from "../../../@/components/blocks/Sidebar";
import { TrendingContractSection } from "./components/trending-table";

export const metadata: Metadata = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { SkeletonContainer } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import {
LogOutIcon,
Expand All @@ -14,7 +15,6 @@ import {
import { useTheme } from "next-themes";
import Link from "next/link";
import { useLayoutEffect, useState } from "react";
import { SkeletonContainer } from "../../../../@/components/ui/skeleton";
import { CmdKSearchModal } from "../../../../components/cmd-k-search";

export function MobileBurgerMenuButton(props: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
Expand All @@ -8,7 +9,6 @@ import {
import { ChevronDownIcon, CommandIcon } from "lucide-react";
import Link from "next/link";
import { useState } from "react";
import { Button } from "../../../../@/components/ui/button";
import { CmdKSearchModal } from "../../../../components/cmd-k-search";

export function ResourcesDropdownButton() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import type { Project } from "@/api/projects";
import type { Team } from "@/api/team";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { useCallback } from "react";
import { useDashboardRouter } from "../../../../@/lib/DashboardRouter";
import { CustomConnectWallet } from "../../../../@3rdweb-sdk/react/components/connect-wallet";
import { useAccount } from "../../../../@3rdweb-sdk/react/hooks/useApi";
import {
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/app/team/[team_slug]/(team)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getProjects } from "@/api/projects";
import { getTeams } from "@/api/team";
import { redirect } from "next/navigation";
import { getProjects } from "../../../../@/api/projects";
import { TeamHeader } from "../../../components/Header/TeamHeader/TeamHeader";
import TeamTabs from "../components/tab-switcher.client";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Button } from "@/components/ui/button";
import { cn } from "../../../../../../../@/lib/utils";
import { cn } from "@/lib/utils";

export function SettingsCard(props: {
bottomText: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TrackedLinkTW } from "../../../../../../@/components/ui/tracked-link";
import { TrackedLinkTW } from "@/components/ui/tracked-link";

type LinkInfo = { label: string; href: string; trackingLabel: string };

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getProject } from "@/api/projects";
import Link from "next/link";
import { notFound } from "next/navigation";
import { getProject } from "../../../../../../@/api/projects";
import { PayPageUI } from "./PayPageUI";

export default async function Page(props: {
Expand Down
Loading

0 comments on commit 9aa5b62

Please sign in to comment.