Trending auctions (#40)
* MainAuctionCard * trending auctions * fix scroll, reduce padding * fixing errors/warnings and formatting * formatting & sold auctions * artists cards
|
@ -2,7 +2,7 @@ import { WalletAdapter } from "@solana/wallet-base";
|
|||
|
||||
import Wallet from "@project-serum/sol-wallet-adapter";
|
||||
import { Button, Modal } from "antd";
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useState} from "react";
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
||||
import { notify } from "./../utils/notifications";
|
||||
import { useConnectionConfig } from "./connection";
|
||||
import { useLocalStorageState } from "./../utils/utils";
|
||||
|
@ -14,20 +14,20 @@ const ASSETS_URL = 'https://raw.githubusercontent.com/solana-labs/oyster/main/as
|
|||
export const WALLET_PROVIDERS = [
|
||||
{
|
||||
name: "Sollet",
|
||||
url: "https://www.sollet.io",
|
||||
url: "https://www.sollet.io",
|
||||
icon: `${ASSETS_URL}sollet.svg`,
|
||||
}, {
|
||||
name: "Solong",
|
||||
url: "https://solongwallet.com",
|
||||
url: "https://solongwallet.com",
|
||||
icon: `${ASSETS_URL}solong.png`,
|
||||
adapter: SolongWalletAdapter,
|
||||
}, {
|
||||
name: "Solflare",
|
||||
url: "https://solflare.com/access-wallet",
|
||||
url: "https://solflare.com/access-wallet",
|
||||
icon: `${ASSETS_URL}solflare.svg`,
|
||||
}, {
|
||||
name: "MathWallet",
|
||||
url: "https://mathwallet.org",
|
||||
url: "https://mathwallet.org",
|
||||
icon: `${ASSETS_URL}mathwallet.svg`,
|
||||
},
|
||||
LedgerProvider,
|
||||
|
@ -47,7 +47,7 @@ const WalletContext = React.createContext<{
|
|||
}>({
|
||||
wallet: undefined,
|
||||
connected: false,
|
||||
select () {},
|
||||
select() { },
|
||||
provider: undefined,
|
||||
});
|
||||
|
||||
|
@ -59,7 +59,7 @@ export function WalletProvider({ children = null as any }) {
|
|||
|
||||
const provider = useMemo(() => WALLET_PROVIDERS.find(({ url }) => url === providerUrl), [providerUrl]);
|
||||
|
||||
const wallet = useMemo(function() {
|
||||
const wallet = useMemo(function () {
|
||||
if (provider) {
|
||||
return new (provider.adapter || Wallet)(providerUrl, endpoint) as WalletAdapter;
|
||||
}
|
||||
|
@ -74,12 +74,12 @@ export function WalletProvider({ children = null as any }) {
|
|||
setConnected(true);
|
||||
const walletPublicKey = wallet.publicKey.toBase58();
|
||||
const keyToDisplay =
|
||||
walletPublicKey.length > 20
|
||||
? `${walletPublicKey.substring(0, 7)}.....${walletPublicKey.substring(
|
||||
walletPublicKey.length - 7,
|
||||
walletPublicKey.length
|
||||
)}`
|
||||
: walletPublicKey;
|
||||
walletPublicKey.length > 20
|
||||
? `${walletPublicKey.substring(0, 7)}.....${walletPublicKey.substring(
|
||||
walletPublicKey.length - 7,
|
||||
walletPublicKey.length
|
||||
)}`
|
||||
: walletPublicKey;
|
||||
|
||||
notify({
|
||||
message: "Wallet update",
|
||||
|
@ -99,19 +99,19 @@ export function WalletProvider({ children = null as any }) {
|
|||
|
||||
return () => {
|
||||
setConnected(false);
|
||||
if(wallet) {
|
||||
if (wallet) {
|
||||
wallet.disconnect();
|
||||
}
|
||||
};
|
||||
}, [wallet]);
|
||||
|
||||
useEffect(() => {
|
||||
if(wallet && autoConnect) {
|
||||
if (wallet && autoConnect) {
|
||||
wallet.connect();
|
||||
setAutoConnect(false);
|
||||
}
|
||||
|
||||
return () => {}
|
||||
return () => { }
|
||||
}, [wallet, autoConnect]);
|
||||
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
|
@ -135,8 +135,8 @@ export function WalletProvider({ children = null as any }) {
|
|||
visible={isModalVisible}
|
||||
okButtonProps={{ style: { display: "none" } }}
|
||||
onCancel={close}
|
||||
width={ 400 }>
|
||||
{WALLET_PROVIDERS.map((provider) => {
|
||||
width={400}>
|
||||
{WALLET_PROVIDERS.map((provider, idx) => {
|
||||
const onClick = function () {
|
||||
setProviderUrl(provider.url);
|
||||
setAutoConnect(true);
|
||||
|
@ -144,24 +144,25 @@ export function WalletProvider({ children = null as any }) {
|
|||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="large"
|
||||
type={ providerUrl === provider.url ? "primary" : "ghost" }
|
||||
onClick={onClick}
|
||||
icon={
|
||||
<img
|
||||
alt={`${provider.name}`}
|
||||
width={ 20 }
|
||||
height={ 20 }
|
||||
src={ provider.icon }
|
||||
style={{ marginRight: 8 }}/>
|
||||
}
|
||||
style={{
|
||||
display: "block",
|
||||
width: "100%",
|
||||
textAlign: "left",
|
||||
marginBottom: 8,
|
||||
}}>{ provider.name }</Button>
|
||||
<Button
|
||||
key={idx}
|
||||
size="large"
|
||||
type={providerUrl === provider.url ? "primary" : "ghost"}
|
||||
onClick={onClick}
|
||||
icon={
|
||||
<img
|
||||
alt={`${provider.name}`}
|
||||
width={20}
|
||||
height={20}
|
||||
src={provider.icon}
|
||||
style={{ marginRight: 8 }} />
|
||||
}
|
||||
style={{
|
||||
display: "block",
|
||||
width: "100%",
|
||||
textAlign: "left",
|
||||
marginBottom: 8,
|
||||
}}>{provider.name}</Button>
|
||||
)
|
||||
})}
|
||||
</Modal>
|
||||
|
@ -176,10 +177,10 @@ export const useWallet = () => {
|
|||
connected,
|
||||
provider,
|
||||
select,
|
||||
connect () {
|
||||
connect() {
|
||||
wallet ? wallet.connect() : select();
|
||||
},
|
||||
disconnect () {
|
||||
disconnect() {
|
||||
wallet?.disconnect();
|
||||
},
|
||||
};
|
||||
|
|
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 93 KiB |
|
@ -20,7 +20,7 @@
|
|||
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Wormhole Solana Bridge" />
|
||||
<meta name="description" content="Metavinci NFT Marketplace" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
|
@ -35,7 +35,7 @@
|
|||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Metavinci</title>
|
||||
<title>Metavinci NFT Marketplace</title>
|
||||
<style type="text/css">
|
||||
#root {
|
||||
height: 100%;
|
||||
|
@ -61,6 +61,8 @@
|
|||
min-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<link
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"short_name": "Wormhole Solana Bridge",
|
||||
"name": "Wormhole Solana Bridge",
|
||||
"short_name": "Metavinci NFT Marketplace",
|
||||
"name": "Metavinci NFT Marketplace",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-icon-36x36.png",
|
||||
|
|
|
@ -24,7 +24,7 @@ body {
|
|||
|
||||
.App {
|
||||
background: #101010 !important;
|
||||
padding: 0px 72px;
|
||||
padding: 0px 30px;
|
||||
}
|
||||
|
||||
code {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import './index.less';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { SearchBox } from './searchBox';
|
||||
import { Button, Popover } from 'antd';
|
||||
import { ConnectButton, CurrentUserBadge, useWallet,Settings } from '@oyster/common';
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import { ConnectButton, CurrentUserBadge, useWallet } from '@oyster/common';
|
||||
|
||||
const UserActions = () => {
|
||||
return <>
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
.artist-card {
|
||||
margin: 15px auto;
|
||||
background: unset;
|
||||
border: unset;
|
||||
}
|
||||
|
||||
.artist-card img {
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import React from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { Card } from 'antd'
|
||||
|
||||
import { Artist } from '../../types'
|
||||
|
||||
import './index.less'
|
||||
|
||||
export const ArtistCard = ({artist}: {artist: Artist}) => {
|
||||
const history = useHistory()
|
||||
|
||||
const handleCoverClick = async () => {
|
||||
history.push(artist.link)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
className="artist-card"
|
||||
cover={<img alt={artist.name} src={artist.image} onClick={handleCoverClick} />}
|
||||
>
|
||||
<div>{artist.name}</div>
|
||||
<div style={{color: "#ffd691"}}>{artist.itemsAvailable} items available</div>
|
||||
<div style={{color: "#82dfd5"}}>{artist.itemsSold} sold</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
.auction-bid {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
padding: 12px;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import React from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { Card } from 'antd'
|
||||
|
||||
import { Auction } from '../../types'
|
||||
|
||||
import './index.less'
|
||||
|
||||
|
||||
export const AuctionCard = ({ auction, sold }: { auction: Auction, sold?: boolean }) => {
|
||||
const history = useHistory()
|
||||
|
||||
const handleCoverClick = async () => {
|
||||
history.push(auction.link)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
hoverable
|
||||
style={{ width: 210, textAlign: 'left' }}
|
||||
cover={<img alt={auction.name} src={auction.image} onClick={handleCoverClick} />}
|
||||
>
|
||||
<div>{auction.name}</div>
|
||||
<a href={auction.auctionerLink}>{auction.auctionerName}</a>
|
||||
<div className="auction-bid">{sold ? 'SOLD' : 'Highest Bid:'} <span style={{color: '#13c2c2'}}>${auction.highestBid}</span> / ◎{auction.solAmt}</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { Layout, Button, Popover } from 'antd';
|
||||
import { Layout } from 'antd';
|
||||
|
||||
import './../../App.less';
|
||||
import './index.less';
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position-y: center;
|
||||
height: 300px;
|
||||
min-height: 300px;
|
||||
margin-bottom: 1rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
|
|
@ -8,3 +8,11 @@ export interface Auction {
|
|||
link: string,
|
||||
image: string,
|
||||
}
|
||||
|
||||
export interface Artist {
|
||||
name: string,
|
||||
link: string,
|
||||
image: string,
|
||||
itemsAvailable?: number,
|
||||
itemsSold?: number,
|
||||
}
|
||||
|
|
|
@ -8,13 +8,11 @@ section.ant-layout {
|
|||
}
|
||||
|
||||
header.ant-layout-header.App-Bar {
|
||||
// position: absolute;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 80px;
|
||||
|
||||
}
|
||||
|
||||
.custom-card {
|
||||
|
@ -27,10 +25,14 @@ header.ant-layout-header.App-Bar {
|
|||
}
|
||||
|
||||
footer.ant-layout-footer {
|
||||
padding: 30px 0;
|
||||
padding: 10px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ant-layout-sider {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: #b480eb;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import React from 'react'
|
||||
import { Layout, Card, Avatar } from 'antd'
|
||||
import { Layout, Row, Col } from 'antd'
|
||||
|
||||
import { Auction } from '../../types'
|
||||
import { Artist, Auction } from '../../types'
|
||||
import { MainAuctionCard } from '../../components/MainAuctionCard'
|
||||
import { AuctionCard } from '../../components/AuctionCard'
|
||||
import { ArtistCard } from '../../components/ArtistCard'
|
||||
|
||||
import './index.less'
|
||||
import { ArtCard } from '../../components/ArtCard'
|
||||
const { Meta } = Card;
|
||||
|
||||
const { Content, Sider } = Layout
|
||||
|
||||
|
@ -19,22 +20,136 @@ const sampleAuction: Auction = {
|
|||
image: "http://localhost:3000/img/auction1.jpg",
|
||||
}
|
||||
|
||||
const sampleAuctions: Array<Auction> = [
|
||||
{
|
||||
name: "Team Trees",
|
||||
auctionerName: "NFToros",
|
||||
auctionerLink: "/address/4321dcba",
|
||||
highestBid: 23000,
|
||||
solAmt: 115,
|
||||
link: "/auction/1234abcd",
|
||||
image: "http://localhost:3000/img/auction2.jpg",
|
||||
},
|
||||
{
|
||||
name: "Miko 4",
|
||||
auctionerName: "Hello World",
|
||||
auctionerLink: "/address/4321dcba",
|
||||
highestBid: 13000,
|
||||
solAmt: 75,
|
||||
link: "/auction/1234abcd",
|
||||
image: "http://localhost:3000/img/auction3.jpg",
|
||||
},
|
||||
{
|
||||
name: "Tell Me",
|
||||
auctionerName: "Supper Club",
|
||||
auctionerLink: "/address/4321dcba",
|
||||
highestBid: 24000,
|
||||
solAmt: 120,
|
||||
link: "/auction/1234abcd",
|
||||
image: "http://localhost:3000/img/auction4.jpg",
|
||||
},
|
||||
{
|
||||
name: "Saucy",
|
||||
auctionerName: "Mr. Momo",
|
||||
auctionerLink: "/address/4321dcba",
|
||||
highestBid: 23000,
|
||||
solAmt: 200,
|
||||
link: "/auction/1234abcd",
|
||||
image: "http://localhost:3000/img/auction5.jpg",
|
||||
},
|
||||
{
|
||||
name: "Haze",
|
||||
auctionerName: "Daily Dose",
|
||||
auctionerLink: "/address/4321dcba",
|
||||
highestBid: 23000,
|
||||
solAmt: 200,
|
||||
link: "/auction/1234abcd",
|
||||
image: "http://localhost:3000/img/auction6.jpg",
|
||||
},
|
||||
{
|
||||
name: "Wounderground",
|
||||
auctionerName: "The Maze",
|
||||
auctionerLink: "/address/4321dcba",
|
||||
highestBid: 23000,
|
||||
solAmt: 200,
|
||||
link: "/auction/1234abcd",
|
||||
image: "http://localhost:3000/img/auction7.jpg",
|
||||
},
|
||||
]
|
||||
|
||||
const sampleArtists: Array<Artist> = [
|
||||
{
|
||||
name: "Yuzu415",
|
||||
link: "/artist/1234abcd",
|
||||
image: "http://localhost:3000/img/artist1.jpeg",
|
||||
itemsAvailable: 7,
|
||||
itemsSold: 215,
|
||||
},
|
||||
{
|
||||
name: "Mischa",
|
||||
link: "/artist/1234abcd",
|
||||
image: "http://localhost:3000/img/artist2.jpeg",
|
||||
itemsAvailable: 2,
|
||||
itemsSold: 215,
|
||||
},
|
||||
{
|
||||
name: "Sammy",
|
||||
link: "/artist/1234abcd",
|
||||
image: "http://localhost:3000/img/artist3.jpeg",
|
||||
itemsAvailable: 7,
|
||||
itemsSold: 215,
|
||||
},
|
||||
{
|
||||
name: "Wonderful",
|
||||
link: "/artist/1234abcd",
|
||||
image: "http://localhost:3000/img/artist4.jpeg",
|
||||
itemsAvailable: 7,
|
||||
itemsSold: 215,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
export const HomeView = () => {
|
||||
// TODO: fetch real data
|
||||
const auction: Auction = sampleAuction
|
||||
const auctions: Array<Auction> = sampleAuctions
|
||||
const soldAuctions: Array<Auction> = sampleAuctions.slice(0, 3)
|
||||
const artists: Array<Artist> = sampleArtists
|
||||
|
||||
return (
|
||||
<Layout style={{ margin: 0 }}>
|
||||
<MainAuctionCard auction={sampleAuction} />
|
||||
<MainAuctionCard auction={auction} />
|
||||
<Layout>
|
||||
<Content style={{ display: 'flex', justifyContent: 'space-between', alignContent: 'space-between', flexWrap: 'wrap' }}>
|
||||
<ArtCard />
|
||||
<ArtCard />
|
||||
<ArtCard />
|
||||
<ArtCard />
|
||||
<ArtCard />
|
||||
<ArtCard />
|
||||
<ArtCard />
|
||||
<ArtCard />
|
||||
<Content style={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||
<Col>
|
||||
<Row style={{ marginBottom: 10 }}>Trending Auctions</Row>
|
||||
<Row gutter={16} justify="space-around" style={{ marginRight: 0 }}>
|
||||
{auctions.map((auction, idx) => (
|
||||
<Col key={idx} className="gutter-row" style={{ marginBottom: 15 }}>
|
||||
<AuctionCard auction={auction} />
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
<Row style={{ marginBottom: 10 }}>Recently Sold</Row>
|
||||
<Row gutter={16} justify="space-around" style={{ marginRight: 0 }}>
|
||||
{soldAuctions.map((auction, idx) => (
|
||||
<Col key={idx} className="gutter-row" style={{ marginBottom: 15 }}>
|
||||
<AuctionCard auction={auction} sold={true} />
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</Col>
|
||||
</Content>
|
||||
<Sider breakpoint="md" collapsedWidth="0">
|
||||
<Col>
|
||||
<Row style={{ marginBottom: 10 }}>Top Artists</Row>
|
||||
{artists.map((artist, idx) => (
|
||||
<Row key={idx} style={{ backgroundColor: "#222222" }}>
|
||||
<ArtistCard artist={artist} />
|
||||
</Row>
|
||||
))}
|
||||
</Col>
|
||||
</Sider>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
|
|