Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/add claim page #1341

Merged
merged 5 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
8 changes: 5 additions & 3 deletions src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ w3m-modal {
display: none;
}

.modal-left, .modal-left-title {
.modal-left,
.modal-left-title {
border-right: 0 !important;
width: 100% !important;
}
Expand All @@ -93,7 +94,8 @@ w3m-modal {
}

@media (max-width: 576px) {
.nws-modal, .modal-left {
.nws-modal,
.modal-left {
max-height: 280px !important;
}
}
}
Loading