Skip to content

Commit

Permalink
Add Ecosystem wallet pages in teams
Browse files Browse the repository at this point in the history
  • Loading branch information
MananTank committed Sep 9, 2024
1 parent 7d508aa commit a9e1e00
Show file tree
Hide file tree
Showing 17 changed files with 311 additions and 202 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { COOKIE_ACTIVE_ACCOUNT, COOKIE_PREFIX_TOKEN } from "@/constants/cookie";
import { BookMarkedIcon, ExternalLinkIcon, MoveRightIcon } from "lucide-react";
import { cookies } from "next/headers";
import Image from "next/image";
import Link from "next/link";
import { redirect } from "next/navigation";
import headerImage from "./assets/header.png";
import { fetchEcosystemList } from "./utils/fetchEcosystemList";

export async function EcosystemLandingPage(props: {
ecosystemLayoutPath: string;
}) {
const cookiesManager = cookies();
const activeAccount = cookiesManager.get(COOKIE_ACTIVE_ACCOUNT)?.value;
const authToken = activeAccount
? cookiesManager.get(`${COOKIE_PREFIX_TOKEN}${activeAccount}`)?.value
: null;

// if user is logged in and has an ecosystem, redirect to the first ecosystem
if (authToken) {
const ecosystems = await fetchEcosystemList(authToken).catch((err) => {
console.error("failed to fetch ecosystems", err);
return [];
});
if (ecosystems.length > 0) {
redirect(`${props.ecosystemLayoutPath}/${ecosystems[0].slug}`);
}
}

// otherwise we fall through to the page

return (
<main className="container flex flex-col max-w-2xl gap-8 pb-6 mx-auto">
<Image
src={headerImage}
alt="Ecosystems"
sizes="100vw"
className="w-full"
/>
<div className="flex flex-col gap-2 text-left">
<h2 className="text-2xl font-bold sm:text-3xl text-foreground text-balance">
One wallet, a whole ecosystem of apps and games
</h2>
<p className="text-muted-foreground">
With Ecosystem Wallets, your users can access their assets across
hundreds of apps and games within your ecosystem. You can control
which apps join your ecosystem and how their users interact with your
wallet.
</p>
</div>
<div className="flex flex-col gap-2 sm:flex-row sm:gap-4">
{!authToken ? (
<ToolTipLabel label="Connect your wallet to create an ecosystem">
<Button variant="primary" className="opacity-50">
Create Ecosystem
</Button>
</ToolTipLabel>
) : (
<Button variant="primary" asChild>
<Link href={`${props.ecosystemLayoutPath}/create`}>
Create Ecosystem
</Link>
</Button>
)}
<Button variant="outline" asChild>
<Link
href="https://portal.thirdweb.com/connect/ecosystems/overview"
target="_blank"
className="flex flex-row gap-2"
>
Read The Docs
<ExternalLinkIcon className="size-4" />
</Link>
</Button>
</div>
<div className="w-full h-0 my-2 border-t" />
<Link
href="https://portal.thirdweb.com/connect/ecosystems/overview"
className="mb-6 group"
>
<Card className="flex flex-col gap-3 p-6 md:p-8 md:px-10">
<div className="flex items-center gap-2">
<BookMarkedIcon className="size-5 text-muted-foreground" />
<h4 className="text-lg font-bold">Learn more</h4>
</div>
<p className="text-muted-foreground text-md">
Learn how to create and manage Ecosystem Wallets in our docs.
</p>
<div className="flex flex-row items-center gap-2 mt-4 transition-all group-hover:gap-3 group-hover:text-primary/80 text-primary text-md">
View Docs <MoveRightIcon className="size-4" />
</div>
</Card>
</Link>
</main>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { COOKIE_ACTIVE_ACCOUNT, COOKIE_PREFIX_TOKEN } from "@/constants/cookie";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { getAddress } from "thirdweb";
import { fetchEcosystem } from "../../utils/fetchEcosystem";
import { EcosystemHeader } from "./components/client/ecosystem-header.client";

export async function EcosystemLayoutSlug({
children,
params,
ecosystemLayoutPath,
}: {
children: React.ReactNode;
params: { slug: string };
ecosystemLayoutPath: string;
}) {
const cookiesManager = cookies();
const activeAccount = cookiesManager.get(COOKIE_ACTIVE_ACCOUNT)?.value;
const authToken = activeAccount
? cookies().get(COOKIE_PREFIX_TOKEN + getAddress(activeAccount))?.value
: null;

if (!authToken) {
redirect(ecosystemLayoutPath);
}

const ecosystem = await fetchEcosystem(params.slug, authToken);

if (!ecosystem) {
redirect(ecosystemLayoutPath);
}

return (
<div className="flex flex-col w-full gap-10 px-2 py-10 sm:px-4">
<EcosystemHeader
ecosystem={ecosystem}
ecosystemLayoutPath={ecosystemLayoutPath}
/>
{children}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ function EcosystemAlertBanner({ ecosystem }: { ecosystem: Ecosystem }) {
}
}

function EcosystemSelect(props: { ecosystem: Ecosystem }) {
function EcosystemSelect(props: {
ecosystem: Ecosystem;
ecosystemLayoutPath: string;
}) {
const { data: ecosystems, isLoading } = useEcosystemList();

return isLoading ? (
Expand All @@ -81,7 +84,7 @@ function EcosystemSelect(props: { ecosystem: Ecosystem }) {
{ecosystems?.map((ecosystem) => (
<DropdownMenuItem key={ecosystem.id} asChild>
<Link
href={`/dashboard/connect/ecosystem/${ecosystem.slug}`}
href={`${props.ecosystemLayoutPath}/${ecosystem.slug}`}
className="relative flex items-center pl-8 pr-3 cursor-pointer"
>
{ecosystem.slug === props.ecosystem.slug && (
Expand All @@ -93,7 +96,7 @@ function EcosystemSelect(props: { ecosystem: Ecosystem }) {
))}
</DropdownMenuGroup>
<DropdownMenuSeparator />
<Link href="/dashboard/connect/ecosystem/create" className="">
<Link href={`${props.ecosystemLayoutPath}/create`} className="">
<DropdownMenuItem className="relative flex items-center pl-8 pr-3 cursor-pointer">
<PlusCircleIcon className="absolute w-4 h-4 left-2" />
<div className="truncate">New Ecosystem</div>
Expand All @@ -104,7 +107,10 @@ function EcosystemSelect(props: { ecosystem: Ecosystem }) {
);
}

export function EcosystemHeader(props: { ecosystem: Ecosystem }) {
export function EcosystemHeader(props: {
ecosystem: Ecosystem;
ecosystemLayoutPath: string;
}) {
const pathname = usePathname();
const { data: fetchedEcosystem } = useEcosystem({
slug: props.ecosystem.slug,
Expand Down Expand Up @@ -166,14 +172,17 @@ export function EcosystemHeader(props: { ecosystem: Ecosystem }) {
</div>
</div>
<div className="flex flex-col justify-between gap-4 md:items-end">
<EcosystemSelect ecosystem={ecosystem} />
<EcosystemSelect
ecosystem={ecosystem}
ecosystemLayoutPath={props.ecosystemLayoutPath}
/>
</div>
</div>
<TabLinks
links={[
{
name: "Permissions",
href: `/dashboard/connect/ecosystem/${ecosystem.slug}/permissions`,
href: `${props.ecosystemLayoutPath}/${ecosystem.slug}/permissions`,
isActive: pathname?.endsWith("/permissions") || false, // pathname will almost never be null: https://nextjs.org/docs/app/api-reference/functions/use-pathname
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,16 @@
import { COOKIE_ACTIVE_ACCOUNT, COOKIE_PREFIX_TOKEN } from "@/constants/cookie";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { getAddress } from "thirdweb";
import type { Ecosystem } from "../../types";
import { EcosystemHeader } from "./components/client/ecosystem-header.client";

async function fetchEcosystem(slug: string, authToken: string) {
const res = await fetch(
`${process.env.NEXT_PUBLIC_THIRDWEB_API_HOST || "https://api.thirdweb.com"}/v1/ecosystem-wallet/${slug}`,
{
headers: {
Authorization: `Bearer ${authToken}`,
},
},
);
if (!res.ok) {
const data = await res.json();
console.error(data);
return null;
}

const data = (await res.json()) as { result: Ecosystem };
return data.result;
}
import { EcosystemLayoutSlug } from "./EcosystemSlugLayout";

export default async function Layout({
children,
params,
}: { children: React.ReactNode; params: { slug: string } }) {
const cookiesManager = cookies();
const activeAccount = cookiesManager.get(COOKIE_ACTIVE_ACCOUNT)?.value;
const authToken = activeAccount
? cookies().get(COOKIE_PREFIX_TOKEN + getAddress(activeAccount))?.value
: null;

if (!authToken) {
redirect("/dashboard/connect/ecosystem");
}

const ecosystem = await fetchEcosystem(params.slug, authToken);

if (!ecosystem) {
redirect("/dashboard/connect/ecosystem");
}

return (
<div className="flex flex-col w-full gap-10 px-2 py-10 sm:px-4">
<EcosystemHeader ecosystem={ecosystem} />
<EcosystemLayoutSlug
params={params}
ecosystemLayoutPath="/dashboard/connect/ecosystem"
>
{children}
</div>
</EcosystemLayoutSlug>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use client";
import { EcosystemPartnersSection } from "../components/server/ecosystem-partners-section";
import { IntegrationPermissionsSection } from "../components/server/integration-permissions-section";
import { useEcosystem } from "../hooks/use-ecosystem";

export function EcosystemPermissionsPage({
params,
}: { params: { slug: string } }) {
const { ecosystem } = useEcosystem({ slug: params.slug });

return (
<div className="flex flex-col gap-12">
<IntegrationPermissionsSection ecosystem={ecosystem} />
{ecosystem?.permission === "PARTNER_WHITELIST" && (
<EcosystemPartnersSection ecosystem={ecosystem} />
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
"use client";
import { EcosystemPartnersSection } from "../components/server/ecosystem-partners-section";
import { IntegrationPermissionsSection } from "../components/server/integration-permissions-section";
import { useEcosystem } from "../hooks/use-ecosystem";
import { EcosystemPermissionsPage } from "./EcosystemPermissionsPage";

export default function Page({ params }: { params: { slug: string } }) {
const { ecosystem } = useEcosystem({ slug: params.slug });

return (
<main className="flex flex-col gap-12">
<IntegrationPermissionsSection ecosystem={ecosystem} />
{ecosystem?.permission === "PARTNER_WHITELIST" && (
<EcosystemPartnersSection ecosystem={ecosystem} />
)}
</main>
);
return <EcosystemPermissionsPage params={params} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { CreateEcosystemForm } from "./components/client/create-ecosystem-form.client";
import { EcosystemWalletPricingCard } from "./components/pricing-card";

export function EcosystemCreatePage(props: {
ecosystemLayoutPath: string;
}) {
return (
<div className="flex flex-col w-full gap-10 px-2 py-10 sm:px-4">
<header className="flex flex-col gap-2">
<h2 className="text-4xl font-bold text-foreground">
Create an Ecosystem
</h2>
<p className="text-muted-foreground">
Create wallets that work across every chain and every app.
</p>
</header>
<main className="grid w-full max-w-sm gap-8 md:max-w-lg lg:max-w-4xl xl:gap-12 lg:grid-cols-2">
<section className="flex items-start">
<EcosystemWalletPricingCard />
</section>
<section className="mb-12 lg:px-4">
<CreateEcosystemForm
ecosystemLayoutPath={props.ecosystemLayoutPath}
/>
</section>
</main>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ const formSchema = z.object({
permission: z.union([z.literal("PARTNER_WHITELIST"), z.literal("ANYONE")]),
});

export function CreateEcosystemForm() {
export function CreateEcosystemForm(props: {
ecosystemLayoutPath: string;
}) {
// When set, the confirmation modal is open the this contains the form data to be submitted
const [formDataToBeConfirmed, setFormDataToBeConfirmed] = useState<
z.infer<typeof formSchema> | undefined
Expand All @@ -65,7 +67,7 @@ export function CreateEcosystemForm() {
},
onSuccess: (slug: string) => {
form.reset();
router.push(`/dashboard/connect/ecosystem/${slug}`);
router.push(`${props.ecosystemLayoutPath}/${slug}`);
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,7 @@
import { CreateEcosystemForm } from "./components/client/create-ecosystem-form.client";
import { EcosystemWalletPricingCard } from "./components/pricing-card";
import { EcosystemCreatePage } from "./EcosystemCreatePage";

export default function Page() {
return (
<div className="flex flex-col w-full gap-10 px-2 py-10 sm:px-4">
<header className="flex flex-col gap-2">
<h2 className="text-4xl font-bold text-foreground">
Create an Ecosystem
</h2>
<p className="text-muted-foreground">
Create wallets that work across every chain and every app.
</p>
</header>
<main className="grid w-full max-w-sm gap-8 md:max-w-lg lg:max-w-4xl xl:gap-12 lg:grid-cols-2">
<section className="flex items-start">
<EcosystemWalletPricingCard />
</section>
<section className="mb-12 lg:px-4">
<CreateEcosystemForm />
</section>
</main>
</div>
<EcosystemCreatePage ecosystemLayoutPath="/dashboard/connect/ecosystem" />
);
}
Loading

0 comments on commit a9e1e00

Please sign in to comment.