Skip to content

Commit

Permalink
"see more" button on connect wallet page
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewliu08 committed Sep 19, 2024
1 parent 0db8d6b commit f23894c
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 97 deletions.
160 changes: 102 additions & 58 deletions apps/crepe/src/app/checkout/components/connect-wallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,94 +2,138 @@

import Image from "next/image";
import { useEffect, useState } from "react";
import { Connector, useConnect } from "wagmi";
import { Address } from "viem";
import { Config, Connector, useConnect } from "wagmi";
import { ConnectMutate } from "wagmi/query";

import RightPane from "./right-pane";
import { Button } from "../../components/button";
import { usePaymentInfo } from "../payment-info-context";
import { trpc } from "../trpc";

interface PaymentOption {
name: string;
logoURI: string;
onClick: () => void;
}

export default function ConnectWallet() {
const { connectors, connect } = useConnect();
const { paymentAddress } = usePaymentInfo();
const paymentOptions = usePaymentOptions(paymentAddress);
const [showAll, setShowAll] = useState(false);

const handleConnect = (connector: Connector) => {
try {
connect({ connector });
} catch (e) {
console.error("Error while connecting:", e);
}
};
const displayedOptions = showAll
? paymentOptions
: paymentOptions.slice(0, 3);

return (
<RightPane>
<section aria-labelledby="wallet-options" className="w-2/3 mx-auto">
<WalletOptions connectors={connectors} handleConnect={handleConnect} />
<div className="flex justify-center w-full mt-5">
<Button variant="tertiary">See more</Button>
</div>
<ul className="w-full h-full flex flex-col gap-4">
{displayedOptions.map((option, index) => (
<li key={index}>
<WalletOption
name={option.name}
logoURI={option.logoURI}
onClick={option.onClick}
/>
</li>
))}
</ul>
{paymentOptions.length > 3 && (
<div className="flex justify-center w-full mt-5">
<Button variant="tertiary" onClick={() => setShowAll(!showAll)}>
{showAll ? "See less" : "See more"}
</Button>
</div>
)}
</section>
</RightPane>
);
}

function WalletOptions({
connectors,
handleConnect,
}: {
connectors: readonly Connector[];
handleConnect: (connector: Connector) => void;
}) {
return (
<ul className="w-full h-full flex flex-col gap-4">
{connectors.map((connector, index) => (
<li key={index}>
<WalletOption
connector={connector}
onClick={() => handleConnect(connector)}
/>
</li>
))}
</ul>
function connectorToPaymentOption(
connector: Connector,
connect: ConnectMutate<Config, unknown>
): PaymentOption {
const logo =
connector.id === "walletConnect"
? "/wallet-logos/wallet-connect-logo.png"
: connector.icon || "";

return {
name: connector.name,
logoURI: logo,
onClick: () => {
try {
connect({ connector });
} catch (e) {
console.error("Error while connecting:", e);
}
},
};
}

function usePaymentOptions(paymentAddress: Address): PaymentOption[] {
const { connectors, connect } = useConnect();

const [allPaymentOptions, setAllPaymentOptions] = useState<PaymentOption[]>(
connectors.map((connector) => connectorToPaymentOption(connector, connect))
);

/** Get other payment options like Daimo and Coinbase */
useEffect(() => {
let isMounted = true; // Needed to prevent double rendering

async function getOtherPaymentOptions() {
const res = await trpc.getOtherPaymentOptions.query({
handoffAddr: paymentAddress,
});

if (isMounted) {
const otherPaymentOptions = res.map((option) => ({
name: option.name,
logoURI: option.logoURI,
onClick: () => (window.location.href = option.url),
}));

setAllPaymentOptions((prevOptions) => [
...prevOptions,
...otherPaymentOptions,
]);
}
}

getOtherPaymentOptions();

return () => {
isMounted = false;
};
}, [paymentAddress]);

return allPaymentOptions;
}

function WalletOption({
connector,
name,
logoURI,
onClick,
}: {
connector: Connector;
name: string;
logoURI: string;
onClick: () => void;
}) {
const [ready, setReady] = useState(false);

useEffect(() => {
(async () => {
const provider = await connector.getProvider();
setReady(!!provider);
})();
}, [connector]);

const getLogo = () => {
if (connector.id === "walletConnect") {
return "/wallet-logos/wallet-connect-logo.png";
}
return connector.icon || "";
};

return (
<Button
variant="secondary"
disabled={!ready}
onClick={onClick}
className="w-full"
>
<Button variant="secondary" onClick={onClick} className="w-full">
<div className="flex items-center gap-3">
<Image
src={getLogo()}
alt={`${connector.name} logo`}
src={logoURI}
alt={`${name} logo`}
width={28}
height={28}
className="w-7 h-7"
/>
{connector.name}
{name}
</div>
</Button>
);
Expand Down
20 changes: 4 additions & 16 deletions apps/crepe/src/app/checkout/components/payment-method.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
} from "@daimo/contract";
import Image from "next/image";
import { useEffect, useState } from "react";
import { Address } from "viem";
import { mainnet } from "viem/chains";
import {
useAccount,
Expand Down Expand Up @@ -38,7 +37,8 @@ interface TokenBalance {
}

export default function PaymentMethods() {
const { onchainOrder, merchantToken, updateTransferInfo } = usePaymentInfo();
const { onchainOrder, merchantToken, paymentAddress, updateTransferInfo } =
usePaymentInfo();
const amount = onchainOrder.destinationFinalCallTokenAmount;

const { address } = useAccount();
Expand All @@ -49,24 +49,12 @@ export default function PaymentMethods() {
const { sendTransactionAsync } = useSendTransaction();
const chainId = useChainId();

const [paymentAddress, setPaymentAddress] = useState<Address | null>(null);
const [paymentOptions, setPaymentOptions] = useState<{
tokenBalances: TokenBalance[];
} | null>(null);
const [isLoadingAmounts, setIsLoadingAmounts] = useState(true);
const [showNotification, setShowNotification] = useState(false);

/**
* Get the address to send funds to from the order details.
*/
useEffect(() => {
if (onchainOrder) {
trpc.getPaymentAddress
.query(onchainOrder)
.then(({ handoffAddr }) => setPaymentAddress(handoffAddr));
}
}, [onchainOrder]);

/**
* Get the token balances for the payment options.
*/
Expand Down Expand Up @@ -164,7 +152,7 @@ function PaymentOptionList({
const visibleOptions = tokenBalances.slice(0, 3);

return (
<div>
<>
<ul className="flex flex-col gap-4">
{isLoading ? (
<PaymentMethodsSkeleton />
Expand Down Expand Up @@ -193,7 +181,7 @@ function PaymentOptionList({
onClose={() => setShowSearchPalette(false)}
/>
)}
</div>
</>
);
}

Expand Down
12 changes: 2 additions & 10 deletions apps/crepe/src/app/checkout/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ConnectWallet from "./components/connect-wallet";
import PaymentConfirmation from "./components/payment-confirmation";
import PaymentMethods from "./components/payment-method";
import SummaryPane from "./components/summary-pane";
import { PaymentInfoProvider, usePaymentInfo } from "./payment-info-context";
import { usePaymentInfo } from "./payment-info-context";

function ConnectWalletPage() {
return (
Expand Down Expand Up @@ -45,7 +45,7 @@ function PaymentConfirmationPage() {
);
}

function HomeContent() {
export default function Home() {
const { isConnected } = useAccount();
const { transferInfo } = usePaymentInfo();

Expand All @@ -69,11 +69,3 @@ function HomeContent() {
</div>
);
}

export default function Home() {
return (
<PaymentInfoProvider>
<HomeContent />
</PaymentInfoProvider>
);
}
30 changes: 21 additions & 9 deletions apps/crepe/src/app/checkout/payment-info-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface PaymentInfo {
onchainOrder: OnchainOrder;
item: PaymentItem;
merchantToken: ForeignToken;
paymentAddress: Address;
transferInfo: TransferInfo | null;
updateTransferInfo: (newTransferInfo: TransferInfo) => void;
}
Expand All @@ -57,42 +58,53 @@ export function PaymentInfoProvider({ children }: { children: ReactNode }) {
useEffect(() => {
async function fetchPaymentInfo() {
if (!orderBase64 || !itemBase64) return;
let order: OnchainOrder | undefined = undefined;
let onchainOrder: OnchainOrder | undefined = undefined;
let item: PaymentItem | null = null;
let merchantToken: ForeignToken | undefined = undefined;
let paymentAddress: Address | undefined = undefined;

try {
const decodedOrder = atob(orderBase64);
order = JSON.parse(decodedOrder);
onchainOrder = JSON.parse(decodedOrder);
} catch (error) {
console.error("Error parsing order:", error);
return;
}

try {
const decodedItem = atob(itemBase64);
item = JSON.parse(decodedItem);
} catch (error) {
console.error("Error parsing item JSON:", error);
return;
}

try {
merchantToken = await trpc.getForeignToken.query({
addr: order?.destinationFinalCallTokenAddr as Address,
chainId: Number(order?.destinationChainId),
addr: onchainOrder?.destinationFinalCallTokenAddr as Address,
chainId: Number(onchainOrder?.destinationChainId),
});
} catch (error) {
console.error("Error fetching merchantToken:", error);
return;
}

try {
const decodedItem = atob(itemBase64);
item = JSON.parse(decodedItem);
if (!onchainOrder) return;
const response = await trpc.getPaymentAddress.query(onchainOrder);
paymentAddress = response.handoffAddr;
} catch (error) {
console.error("Error parsing item JSON:", error);
console.error("Error fetching payment address:", error);
return;
}

if (!item || !order || !merchantToken) return;
if (!onchainOrder || !item || !merchantToken || !paymentAddress) return;

setPaymentInfo({
onchainOrder: order,
onchainOrder,
item,
merchantToken,
paymentAddress,
transferInfo: null,
updateTransferInfo,
});
Expand Down
11 changes: 7 additions & 4 deletions apps/crepe/src/app/checkout/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState, type ReactNode } from "react";
import { type ReactNode } from "react";
import { WagmiProvider, type State } from "wagmi";

import { PaymentInfoProvider } from "./payment-info-context";
import { config } from "./wagmi-config";

type Props = {
children: ReactNode;
initialState: State | undefined;
};

export function Providers({ children, initialState }: Props) {
const [queryClient] = useState(() => new QueryClient());
const queryClient = new QueryClient();

export function Providers({ children, initialState }: Props) {
return (
<WagmiProvider config={config} initialState={initialState}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
<QueryClientProvider client={queryClient}>
<PaymentInfoProvider>{children}</PaymentInfoProvider>
</QueryClientProvider>
</WagmiProvider>
);
}

0 comments on commit f23894c

Please sign in to comment.