Skip to content

Commit

Permalink
Merge pull request #1341 from matiasbenary/feat/add-claim-page
Browse files Browse the repository at this point in the history
Feat/add claim page
  • Loading branch information
gagdiez authored Nov 22, 2024
2 parents fd49528 + 53a638e commit aa7bcf6
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 11 deletions.
40 changes: 40 additions & 0 deletions src/assets/images/meteor.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/my_near_wallet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions src/components/claim/FTPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Flex, Text } from '@near-pagoda/ui';
import Image from 'next/image';

type FT = {
decimals: number;
icon: string;
name: string;
symbol: string;
total_supply: string;
};

const FTPreview = ({ token }: { token: FT }) => {
return (
<Flex justify="space-between" align="center" style={{ flexDirection: 'column' }}>
<Text size="text-xl">{token.name}</Text>
<Image src={token.icon} alt={token.name} width={50} height={50} />
<Text>
{(BigInt(token.total_supply) / BigInt(10 ** token.decimals)).toString()} {token.symbol}
</Text>
</Flex>
);
};

export default FTPreview;
35 changes: 35 additions & 0 deletions src/components/claim/NFTPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Flex, Text } from '@near-pagoda/ui';
import { useContext, useEffect, useState } from 'react';

import type { NFT } from '@/utils/types';

import { NftImage } from '../NTFImage';
import { NearContext } from '../wallet-selector/WalletSelector';

const NFTPreview = ({ nft }: { nft: NFT }) => {
const [infoNft, setInfoNft] = useState<NFT | undefined>(undefined);
const { wallet } = useContext(NearContext);
useEffect(() => {
if (!wallet) return;
const fetchNftInfo = async () => {
setInfoNft(
await wallet.viewMethod({
contractId: nft.contract_id,
method: 'nft_token',
args: { token_id: nft.token_id },
}),
);
};
fetchNftInfo();
}, [nft, wallet]);

return (
<Flex justify="space-between" align="center" style={{ flexDirection: 'column' }}>
<Text size="text-xl">{infoNft?.metadata?.title}</Text>
<NftImage nft={infoNft} />
<Text>{infoNft?.metadata?.description}</Text>
</Flex>
);
};

export default NFTPreview;
16 changes: 16 additions & 0 deletions src/components/claim/NearPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Flex, Text } from '@near-pagoda/ui';
import Image from 'next/image';

import NearIconSvg from '@/assets/images/near-icon.svg';

const NearPreview = ({ amount }: { amount: string }) => {
return (
<Flex justify="space-between" align="center" style={{ flexDirection: 'column' }}>
<Text size="text-xl">NEAR</Text>
<Image src={NearIconSvg} alt="NEAR" width={50} height={50} />
<Text>{amount} NEAR</Text>
</Flex>
);
};

export default NearPreview;
54 changes: 54 additions & 0 deletions src/components/claim/Wallets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Flex } from '@near-pagoda/ui';
import Image from 'next/image';
import styled from 'styled-components';

import Meteor from '@/assets/images/meteor.svg';
import MyNearWallet from '@/assets/images/my_near_wallet.png';
import { networkId } from '@/config';

const StyledButton = styled.a`
background-color: #1e2030;
color: #fff;
border: none;
border-radius: 25px;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.3s;
&:hover {
background-color: #101124;
}
`;

const WalletButton = styled(StyledButton)`
background-color: #212529;
text-decoration: none;
&:hover {
background-color: #11111c;
}
`;

const Wallets = ({ url }: { url: string }) => {
// https://docs.keypom.xyz/docs/next/keypom-sdk/Core/modules#supportedlinkdropclaimpages
const meteorWalletUrl = 'https://wallet.meteorwallet.app/linkdrop/';
const myNearWalletUrl =
networkId === 'testnet' ? 'https://testnet.mynearwallet.com/linkdrop/' : 'https://app.mynearwallet.com/linkdrop/';

return (
<Flex stack style={{ paddingTop: '1rem' }} gap="m">
<WalletButton href={`${meteorWalletUrl}${url}`} target="_blank">
<Image height={20} alt="Mintbase Logo" src={Meteor} />
<span style={{ textDecoration: 'none', marginLeft: '1rem' }}>Meteor Wallet</span>
</WalletButton>
<WalletButton href={`${myNearWalletUrl}${url}`} target="_blank">
<Image height={19} alt="My Near Wallet Logo" src={MyNearWallet} />
<span style={{ textDecoration: 'none', marginLeft: '1rem' }}>My Near Wallet</span>
</WalletButton>
</Flex>
);
};

export default Wallets;
2 changes: 1 addition & 1 deletion src/components/layouts/DefaultLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const Wrapper = styled.div<{
min-width: 0;
justify-content: stretch;
align-items: stretch;
flex-direction: ${(p) => (p.$sidebar ? 'row' : 'column')};
flex-direction: row;
@media (max-width: ${SMALL_SCREEN_LAYOUT_MAX_WIDTH}px) {
--sidebar-width-expanded: 100vw;
Expand Down
12 changes: 6 additions & 6 deletions src/components/tools/Linkdrops/ListTokenDrop.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Accordion, Badge, Button, copyTextToClipboard, Flex, Text, Title, Tooltip } from '@near-pagoda/ui';
import { Copy } from '@phosphor-icons/react';

import { networkId } from '@/config';
import { network } from '@/config';
import type { Drops } from '@/utils/types';

const getDropName = (drop: Drops) => {
Expand Down Expand Up @@ -37,11 +37,11 @@ const ListTokenDrop = ({ drops }: { drops: Drops[] }) => {
<Button
label="copy"
onClick={() => {
const url =
`https://${networkId === 'mainnet' ? 'app' : 'testnet'}.mynearwallet.com` +
`/linkdrop/v2.keypom.${networkId === 'mainnet' ? 'near' : 'testnet'}/` +
key.private;
copyTextToClipboard(url, url);
if (typeof window !== 'undefined') {
const currentOrigin = window.location.origin;
const url = `${currentOrigin}/claim/${network.linkdrop}/${key.private}`;
copyTextToClipboard(url, url);
}
}}
size="small"
fill="outline"
Expand Down
2 changes: 1 addition & 1 deletion src/components/tools/Shared/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const Carousel: React.FC<CarouselProps> = ({ nfts, onSelect = empty, nftSelected
return (
<CarouselContainer>
{nfts.map((nft) => (
<Tooltip key={`Carousel-${nft.token_id}`} content={nft.metadata.title} asChild>
<Tooltip key={`Carousel-${nft.token_id}`} content={nft?.metadata?.title} asChild>
<ImgCard onClick={() => onSelect(nft)} $selected={nftSelected === nft}>
<NftImage nft={nft} />
</ImgCard>
Expand Down
149 changes: 149 additions & 0 deletions src/pages/claim/[contract_id]/[key].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { Button, Card, Container, Flex, Section, Text } from '@near-pagoda/ui';
import { KeyPair } from 'near-api-js';
import { formatNearAmount } from 'near-api-js/lib/utils/format';
import { useRouter } from 'next/router';
import React, { useContext, useEffect, useState } from 'react';

import FTPreview from '@/components/claim/FTPreview';
import NearPreview from '@/components/claim/NearPreview';
import NFTPreview from '@/components/claim/NFTPreview';
import Wallets from '@/components/claim/Wallets';
import { NearContext } from '@/components/wallet-selector/WalletSelector';
import { useDefaultLayout } from '@/hooks/useLayout';
import type { NextPageWithLayout, NFT } from '@/utils/types';

export type KeyPairString = `ed25519:${string}` | `secp256k1:${string}`;

type FT = {
decimals: number;
icon: string;
name: string;
symbol: string;
total_supply: string;
};

type DropData = {
token?: FT;
nft?: NFT;
amount?: string;
};

const ToolsPage: NextPageWithLayout = () => {
const router = useRouter();
const { contract_id, key } = router.query;
const { signedAccountId, wallet } = useContext(NearContext);

const [dropData, setDropData] = useState<DropData>({});
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
const fetchDropData = async () => {
if (!wallet || !signedAccountId || !contract_id || !key) {
setIsLoading(false);
return;
}

try {
setIsLoading(true);
setError(null);
const pk = KeyPair.fromString(key as KeyPairString);
const dropInformation = await wallet.viewMethod({
contractId: contract_id as string,
method: 'get_drop_information',
args: { key: pk.getPublicKey().toString() },
});

if (dropInformation.ft) {
const metadata = await wallet.viewMethod({
contractId: dropInformation.ft.contract_id,
method: 'ft_metadata',
});
setDropData({
token: {
...metadata,
total_supply: dropInformation.ft.balance_per_use,
},
});
} else if (dropInformation.nft) {
const nftId = await wallet.viewMethod({
contractId: contract_id as string,
method: 'get_nft_token_ids_for_drop',
args: { drop_id: dropInformation.drop_id },
});
setDropData({
nft: {
contract_id: dropInformation.nft.contract_id,
token_id: nftId[0],
},
});
} else {
const balance = await wallet.viewMethod({
contractId: contract_id as string,
method: 'get_key_balance',
args: { key: pk.getPublicKey().toString() },
});
setDropData({ amount: formatNearAmount(balance) });
}
} catch (err) {
console.error(err);
setError('Failed to fetch drop data. Please try again.');
} finally {
setIsLoading(false);
}
};

fetchDropData();
}, [wallet, signedAccountId, contract_id, key]);

const renderDropContent = () => {
const { token, nft, amount } = dropData;

if (token) {
return <FTPreview token={token} />;
}

if (nft) {
return <NFTPreview nft={nft} />;
}

if (amount) {
return <NearPreview amount={amount} />;
}

return null;
};

return (
<Section grow="available" style={{ background: 'var(--sand3)' }}>
<Container size="s">
<Flex stack gap="l">
<Text as="h1" size="text-2xl">
Claim
</Text>
<Card style={{ padding: '2rem' }}>
{isLoading ? (
<Text>Loading the drop</Text>
) : error ? (
<Text>Error</Text>
) : signedAccountId ? (
<>
{renderDropContent()}
<Wallets url={`${contract_id}/${key}`}></Wallets>
</>
) : (
<>
<Text>Please sign in to use wallet utilities</Text>
<Button label="Sign In" fill="outline" onClick={() => wallet?.signIn()} />
</>
)}
</Card>
</Flex>
</Container>
</Section>
);
};

ToolsPage.getLayout = useDefaultLayout;

export default ToolsPage;
6 changes: 3 additions & 3 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,16 @@ export type FT = {
export interface NFT {
contract_id: string;
token_id: string;
owner_id: string;
metadata: {
owner_id?: string;
metadata?: {
title: string;
description: string | null;
media: string | null;
copies: string | null;
reference: string | null;
base_uri: string | null;
};
approved_account_ids: string[] | null;
approved_account_ids?: string[] | null;
}

export type Collection = {
Expand Down

0 comments on commit aa7bcf6

Please sign in to comment.