mirror of https://github.com/certusone/oyster.git
feat: create auction
This commit is contained in:
parent
825aca40ef
commit
523f01a719
|
@ -1,21 +1,12 @@
|
|||
import React, { useLayoutEffect, useState } from 'react';
|
||||
import { Card, Avatar } from 'antd';
|
||||
import { Card, Avatar, CardProps } from 'antd';
|
||||
import { MetadataCategory } from '@oyster/common';
|
||||
import { ArtContent } from './../ArtContent';
|
||||
import './index.less';
|
||||
|
||||
const { Meta } = Card;
|
||||
|
||||
export const ArtCard = ({
|
||||
image,
|
||||
category,
|
||||
name,
|
||||
symbol,
|
||||
description,
|
||||
artist,
|
||||
preview,
|
||||
small,
|
||||
}: {
|
||||
export interface ArtCardProps extends CardProps {
|
||||
image?: string;
|
||||
category?: MetadataCategory
|
||||
name?: string;
|
||||
|
@ -23,13 +14,17 @@ export const ArtCard = ({
|
|||
description?: string;
|
||||
artist?: string;
|
||||
preview?: boolean;
|
||||
small?: boolean
|
||||
}) => {
|
||||
small?: boolean;
|
||||
}
|
||||
|
||||
export const ArtCard = (props: ArtCardProps) => {
|
||||
const { className, small, category, image, name, preview, artist, ...rest } = props;
|
||||
return (
|
||||
<Card
|
||||
hoverable={true}
|
||||
className={`art-card ${small ? 'small' : ''}`}
|
||||
cover={<ArtContent category={category} content={image} />}
|
||||
className={`art-card ${small ? 'small' : ''} ${className}`}
|
||||
cover={<ArtContent category={category} content={image} preview={preview} />}
|
||||
{...rest}
|
||||
>
|
||||
<Meta
|
||||
title={`${name}`}
|
||||
|
|
|
@ -2,11 +2,27 @@ import React, { useMemo } from 'react';
|
|||
import { Image } from 'antd';
|
||||
import { MetadataCategory } from '@oyster/common'
|
||||
|
||||
export const ArtContent = ({ content, category, className }: { category?: MetadataCategory, content?: string, className?: string }) => {
|
||||
export const ArtContent = ({
|
||||
content,
|
||||
category,
|
||||
className,
|
||||
preview
|
||||
}: {
|
||||
category?: MetadataCategory,
|
||||
content?: string,
|
||||
className?: string,
|
||||
preview?: boolean,
|
||||
}) => {
|
||||
return category === 'video' ?
|
||||
<video src={content} className={className} playsInline={true} autoPlay={true} controlsList="nodownload" loop={true} /> :
|
||||
<video src={content}
|
||||
className={className}
|
||||
playsInline={true}
|
||||
autoPlay={true}
|
||||
controlsList="nodownload"
|
||||
loop={true} /> :
|
||||
<Image
|
||||
src={content}
|
||||
preview={preview}
|
||||
wrapperClassName={className}
|
||||
/>;
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export * from './useArt';
|
||||
export * from './useAuctions';
|
||||
export * from './useUserArts';
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { TokenAccount, useUserAccounts } from '@oyster/common';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useMeta } from './../contexts';
|
||||
|
||||
export const useUserArts = () => {
|
||||
const { metadata } = useMeta();
|
||||
const { userAccounts } = useUserAccounts();
|
||||
const accountByMint = userAccounts.reduce((prev, acc) => {
|
||||
prev.set(acc.info.mint.toBase58(), acc);
|
||||
return prev;
|
||||
}, new Map<string, TokenAccount>());
|
||||
|
||||
const ownedMetadata = metadata.filter(m => accountByMint.has(m.info.mint.toBase58()));
|
||||
|
||||
return ownedMetadata;
|
||||
}
|
|
@ -1,20 +1,12 @@
|
|||
import { ParsedAccount, TokenAccount, useUserAccounts } from '@oyster/common';
|
||||
import React from 'react';
|
||||
import { ArtCard } from '../../components/ArtCard';
|
||||
import { useMeta } from '../../contexts';
|
||||
import { Row, Col } from 'antd';
|
||||
import Masonry from 'react-masonry-css'
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useUserArts } from '../../hooks';
|
||||
|
||||
export const ArtworksView = () => {
|
||||
const { metadata } = useMeta();
|
||||
const { userAccounts } = useUserAccounts();
|
||||
const accountByMint = userAccounts.reduce((prev, acc) => {
|
||||
prev.set(acc.info.mint.toBase58(), acc);
|
||||
return prev;
|
||||
}, new Map<string, TokenAccount>());
|
||||
|
||||
const ownedMetadata = metadata.filter(m => accountByMint.has(m.info.mint.toBase58()));
|
||||
const ownedMetadata = useUserArts();
|
||||
const breakpointColumnsObj = {
|
||||
default: 4,
|
||||
1100: 3,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Steps,
|
||||
Row,
|
||||
|
@ -12,28 +12,29 @@ import {
|
|||
Spin,
|
||||
InputNumber,
|
||||
Select,
|
||||
TimePicker,
|
||||
DatePicker,
|
||||
} from 'antd';
|
||||
import { ArtCard } from './../../components/ArtCard';
|
||||
import { UserSearch, UserValue } from './../../components/UserSearch';
|
||||
import { Confetti } from './../../components/Confetti';
|
||||
import './../styles.less';
|
||||
import { mintNFT } from '../../models';
|
||||
import {
|
||||
MAX_METADATA_LEN,
|
||||
MAX_OWNER_LEN,
|
||||
MAX_URI_LENGTH,
|
||||
Metadata,
|
||||
NameSymbolTuple,
|
||||
useConnection,
|
||||
useWallet,
|
||||
IMetadataExtension,
|
||||
MetadataCategory,
|
||||
useConnectionConfig,
|
||||
Metadata,
|
||||
ParsedAccount,
|
||||
} from '@oyster/common';
|
||||
import { getAssetCostToStore, LAMPORT_MULTIPLIER } from '../../utils/assets';
|
||||
import { Connection } from '@solana/web3.js';
|
||||
import { Connection, ParsedAccountData, PublicKey } from '@solana/web3.js';
|
||||
import { MintLayout } from '@solana/spl-token';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useUserArts } from '../../hooks';
|
||||
import Masonry from 'react-masonry-css';
|
||||
|
||||
const { Step } = Steps;
|
||||
const { Option } = Select;
|
||||
|
@ -46,6 +47,28 @@ export enum AuctionCategory {
|
|||
Collection
|
||||
}
|
||||
|
||||
export interface AuctionState {
|
||||
// Min price required for the item to sell
|
||||
reservationPrice: number;
|
||||
|
||||
// listed NFTs
|
||||
items: ParsedAccount<Metadata>[];
|
||||
|
||||
// number of editions for this auction (only applicable to limited edition)
|
||||
editions?: number;
|
||||
|
||||
// date time when auction should start UTC+0
|
||||
startDate?: Date;
|
||||
|
||||
// suggested date time when auction should end UTC+0
|
||||
endDate?: Date;
|
||||
|
||||
// time interval between highest bid and end of the auction
|
||||
gapTime?: Date
|
||||
|
||||
category: AuctionCategory;
|
||||
}
|
||||
|
||||
export const AuctionCreateView = () => {
|
||||
const connection = useConnection();
|
||||
const { env } = useConnectionConfig();
|
||||
|
@ -56,15 +79,10 @@ export const AuctionCreateView = () => {
|
|||
const [step, setStep] = useState<number>(0);
|
||||
const [saving, setSaving] = useState<boolean>(false);
|
||||
const [progress, setProgress] = useState<number>(0);
|
||||
const [attributes, setAttributes] = useState<IMetadataExtension>({
|
||||
name: '',
|
||||
symbol: '',
|
||||
description: '',
|
||||
externalUrl: '',
|
||||
image: '',
|
||||
royalty: 0,
|
||||
files: [],
|
||||
category: MetadataCategory.Image,
|
||||
const [attributes, setAttributes] = useState<AuctionState>({
|
||||
reservationPrice: 0,
|
||||
items: [],
|
||||
category: AuctionCategory.Single,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -76,19 +94,9 @@ export const AuctionCreateView = () => {
|
|||
history.push(`/auction/create/${_step.toString()}`)
|
||||
}
|
||||
|
||||
// store files
|
||||
const mint = async () => {
|
||||
const metadata = {
|
||||
...(attributes as any),
|
||||
image: attributes.files && attributes.files?.[0] && attributes.files[0].name,
|
||||
files: (attributes?.files || []).map(f => f.name),
|
||||
}
|
||||
setSaving(true)
|
||||
const inte = setInterval(() => setProgress(prog => prog + 1), 600)
|
||||
// Update progress inside mintNFT
|
||||
await mintNFT(connection, wallet, env, (attributes?.files || []), metadata)
|
||||
clearInterval(inte)
|
||||
}
|
||||
const createAuction = async () => {
|
||||
// TODO: ....
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -109,7 +117,7 @@ export const AuctionCreateView = () => {
|
|||
<Col {...(saving ? { xl: 24 } : { xl: 16 })}>
|
||||
{step === 0 && (
|
||||
<CategoryStep
|
||||
confirm={(category: MetadataCategory) => {
|
||||
confirm={(category: AuctionCategory) => {
|
||||
setAttributes({
|
||||
...attributes,
|
||||
category,
|
||||
|
@ -127,34 +135,27 @@ export const AuctionCreateView = () => {
|
|||
)}
|
||||
|
||||
{step === 2 && (
|
||||
<InfoStep
|
||||
<TermsStep
|
||||
attributes={attributes}
|
||||
setAttributes={setAttributes}
|
||||
confirm={() => gotoStep(3)}
|
||||
/>
|
||||
)}
|
||||
{step === 3 && (
|
||||
<RoyaltiesStep
|
||||
attributes={attributes}
|
||||
confirm={() => gotoStep(4)}
|
||||
setAttributes={setAttributes}
|
||||
/>
|
||||
)}
|
||||
{step === 4 && (
|
||||
<LaunchStep
|
||||
<ReviewStep
|
||||
attributes={attributes}
|
||||
confirm={() => gotoStep(5)}
|
||||
connection={connection}
|
||||
/>
|
||||
)}
|
||||
{step === 5 && (
|
||||
{step === 4 && (
|
||||
<WaitingStep
|
||||
mint={mint}
|
||||
createAuction={createAuction}
|
||||
progress={progress}
|
||||
confirm={() => gotoStep(6)}
|
||||
/>
|
||||
)}
|
||||
{step === 6 && (
|
||||
{step === 5 && (
|
||||
<Congrats />
|
||||
)}
|
||||
{(0 < step && step < 5) && <Button onClick={() => gotoStep(step - 1)}>Back</Button>}
|
||||
|
@ -164,7 +165,7 @@ export const AuctionCreateView = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const CategoryStep = (props: { confirm: (category: MetadataCategory) => void }) => {
|
||||
const CategoryStep = (props: { confirm: (category: AuctionCategory) => void }) => {
|
||||
return (
|
||||
<>
|
||||
<Row className="call-to-action">
|
||||
|
@ -179,7 +180,7 @@ const CategoryStep = (props: { confirm: (category: MetadataCategory) => void })
|
|||
<Button
|
||||
className="type-btn"
|
||||
size="large"
|
||||
onClick={() => props.confirm(MetadataCategory.Image)}
|
||||
onClick={() => props.confirm(AuctionCategory.Single)}
|
||||
>
|
||||
<div>
|
||||
<div>Single Artwork</div>
|
||||
|
@ -191,7 +192,7 @@ const CategoryStep = (props: { confirm: (category: MetadataCategory) => void })
|
|||
<Button
|
||||
className="type-btn"
|
||||
size="large"
|
||||
onClick={() => props.confirm(MetadataCategory.Video)}
|
||||
onClick={() => props.confirm(AuctionCategory.Limited)}
|
||||
>
|
||||
<div>
|
||||
<div>Limited Edition</div>
|
||||
|
@ -203,7 +204,7 @@ const CategoryStep = (props: { confirm: (category: MetadataCategory) => void })
|
|||
<Button
|
||||
className="type-btn"
|
||||
size="large"
|
||||
onClick={() => props.confirm(MetadataCategory.Audio)}
|
||||
onClick={() => props.confirm(AuctionCategory.Open)}
|
||||
>
|
||||
<div>
|
||||
<div>Open Edition</div>
|
||||
|
@ -216,7 +217,7 @@ const CategoryStep = (props: { confirm: (category: MetadataCategory) => void })
|
|||
disabled={true}
|
||||
className="type-btn"
|
||||
size="large"
|
||||
onClick={() => props.confirm(MetadataCategory.Audio)}
|
||||
onClick={() => props.confirm(AuctionCategory.Collection)}
|
||||
>
|
||||
<div>
|
||||
<div>Collection (Coming Soon)</div>
|
||||
|
@ -231,21 +232,27 @@ const CategoryStep = (props: { confirm: (category: MetadataCategory) => void })
|
|||
};
|
||||
|
||||
const SelectItemsStep = (props: {
|
||||
attributes: IMetadataExtension;
|
||||
setAttributes: (attr: IMetadataExtension) => void;
|
||||
attributes: AuctionState;
|
||||
setAttributes: (attr: AuctionState) => void;
|
||||
confirm: () => void;
|
||||
}) => {
|
||||
const [mainFile, setMainFile] = useState<any>()
|
||||
const [coverFile, setCoverFile] = useState<any>()
|
||||
const [image, setImage] = useState<string>("")
|
||||
const items = useUserArts();
|
||||
const [selectedItems, setSelectedItems] = useState<Set<string>>(new Set(props.attributes.items.map(item => item.pubkey.toBase58())));
|
||||
|
||||
useEffect(() => {
|
||||
props.setAttributes({
|
||||
...props.attributes,
|
||||
files: []
|
||||
// TODO: add items
|
||||
items: items.filter(item => selectedItems.has(item.pubkey.toBase58()))
|
||||
})
|
||||
}, [])
|
||||
}, [selectedItems]);
|
||||
|
||||
const breakpointColumnsObj = {
|
||||
default: 4,
|
||||
1100: 3,
|
||||
700: 2,
|
||||
500: 1
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -256,6 +263,37 @@ const SelectItemsStep = (props: {
|
|||
</p>
|
||||
</Row>
|
||||
<Row className="content-action">
|
||||
<Masonry
|
||||
breakpointCols={breakpointColumnsObj}
|
||||
className="my-masonry-grid"
|
||||
columnClassName="my-masonry-grid_column"
|
||||
>
|
||||
{items.map(m => {
|
||||
const id = m.pubkey.toBase58();
|
||||
const isSelected = selectedItems.has(id);
|
||||
|
||||
const onSelect = () => {
|
||||
let list = [...selectedItems.keys()];
|
||||
if (props.attributes.category !== AuctionCategory.Collection) {
|
||||
list = [];
|
||||
}
|
||||
|
||||
isSelected ?
|
||||
setSelectedItems(new Set(list.filter(item => item !== id))) :
|
||||
setSelectedItems(new Set([...list, id]));
|
||||
};
|
||||
|
||||
return <ArtCard key={id}
|
||||
image={m.info.extended?.image}
|
||||
category={m.info.extended?.category}
|
||||
name={m.info?.name}
|
||||
symbol={m.info.symbol}
|
||||
preview={false}
|
||||
onClick={onSelect}
|
||||
className={isSelected ? 'selected-card' : ''}
|
||||
/>;
|
||||
})}
|
||||
</Masonry>
|
||||
</Row>
|
||||
<Row>
|
||||
<Button
|
||||
|
@ -264,23 +302,22 @@ const SelectItemsStep = (props: {
|
|||
onClick={() => {
|
||||
props.setAttributes({
|
||||
...props.attributes,
|
||||
files: [mainFile, coverFile].filter(f => f),
|
||||
image,
|
||||
|
||||
})
|
||||
props.confirm()
|
||||
}}
|
||||
className="action-btn"
|
||||
>
|
||||
Continue to Mint
|
||||
Continue to Terms
|
||||
</Button>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const InfoStep = (props: {
|
||||
attributes: IMetadataExtension;
|
||||
setAttributes: (attr: IMetadataExtension) => void;
|
||||
const TermsStep = (props: {
|
||||
attributes: AuctionState;
|
||||
setAttributes: (attr: AuctionState) => void;
|
||||
confirm: () => void;
|
||||
}) => {
|
||||
const [creators, setCreators] = useState<Array<UserValue>>([]);
|
||||
|
@ -288,76 +325,61 @@ const InfoStep = (props: {
|
|||
return (
|
||||
<>
|
||||
<Row className="call-to-action">
|
||||
<h2>Describe your creation</h2>
|
||||
<h2>Specify the terms of your auction</h2>
|
||||
<p>
|
||||
Provide detailed description of your creative process to engage with
|
||||
your audience.
|
||||
Provide detailed auction parameters such as price, start time, etc.
|
||||
</p>
|
||||
</Row>
|
||||
<Row className="content-action">
|
||||
<Col xl={12}>
|
||||
{props.attributes.image && (
|
||||
<ArtCard
|
||||
image={props.attributes.image}
|
||||
category={props.attributes.category}
|
||||
name={props.attributes.name}
|
||||
symbol={props.attributes.symbol}
|
||||
small={true}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
<Col className="section" xl={12}>
|
||||
<Col className="section" xl={24}>
|
||||
<label className="action-field">
|
||||
<span className="field-title">Title</span>
|
||||
<span className="field-title">Price Floor (USD)</span>
|
||||
<Input
|
||||
autoFocus
|
||||
className="input"
|
||||
placeholder="Max 50 characters"
|
||||
placeholder="Enter reservation price"
|
||||
allowClear
|
||||
value={props.attributes.name}
|
||||
onChange={info =>
|
||||
props.setAttributes({
|
||||
...props.attributes,
|
||||
name: info.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<span className="field-info">= ◎ 4.84</span>
|
||||
</label>
|
||||
<label className="action-field">
|
||||
<span className="field-title">Symbol</span>
|
||||
<span className="field-title">Tick Size (USD)</span>
|
||||
<Input
|
||||
className="input"
|
||||
placeholder="Max 10 characters"
|
||||
placeholder="Enter tick size"
|
||||
allowClear
|
||||
value={props.attributes.symbol}
|
||||
onChange={info =>
|
||||
props.setAttributes({
|
||||
...props.attributes,
|
||||
symbol: info.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<span className="field-info">= ◎ 4.84</span>
|
||||
</label>
|
||||
<label className="action-field">
|
||||
<span className="field-title">Creators</span>
|
||||
<UserSearch
|
||||
setCreators={setCreators}
|
||||
/>
|
||||
<span className="field-title">Preview Start Date</span>
|
||||
<DatePicker className="field-date" size="large" />
|
||||
<TimePicker className="field-date" size="large" />
|
||||
</label>
|
||||
<label className="action-field">
|
||||
<span className="field-title">Description</span>
|
||||
<Input.TextArea
|
||||
className="input textarea"
|
||||
placeholder="Max 500 characters"
|
||||
value={props.attributes.description}
|
||||
onChange={info =>
|
||||
props.setAttributes({
|
||||
...props.attributes,
|
||||
description: info.target.value,
|
||||
})
|
||||
}
|
||||
allowClear
|
||||
/>
|
||||
<span className="field-title">When do you want the auction to begin?</span>
|
||||
<span>Immediately</span>
|
||||
<span>At a specified data</span>
|
||||
</label>
|
||||
<label className="action-field">
|
||||
<span className="field-title">Auction Start Date</span>
|
||||
<DatePicker className="field-date" size="large" />
|
||||
<TimePicker className="field-date" size="large" />
|
||||
</label>
|
||||
<label className="action-field">
|
||||
<span className="field-title">End Start Date</span>
|
||||
<DatePicker className="field-date" size="large" />
|
||||
<TimePicker className="field-date" size="large" />
|
||||
</label>
|
||||
</Col>
|
||||
</Row>
|
||||
|
@ -368,81 +390,18 @@ const InfoStep = (props: {
|
|||
onClick={props.confirm}
|
||||
className="action-btn"
|
||||
>
|
||||
Continue to royalties
|
||||
Continue to Review
|
||||
</Button>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const RoyaltiesStep = (props: {
|
||||
attributes: IMetadataExtension;
|
||||
setAttributes: (attr: IMetadataExtension) => void;
|
||||
const ReviewStep = (props: {
|
||||
confirm: () => void;
|
||||
}) => {
|
||||
const file = props.attributes.image;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row className="call-to-action">
|
||||
<h2>Set royalties for the creation</h2>
|
||||
<p>
|
||||
A royalty is a payment made by the seller of this item to the creator.
|
||||
It is charged after every successful auction.
|
||||
</p>
|
||||
</Row>
|
||||
<Row className="content-action">
|
||||
<Col xl={12}>
|
||||
{file && (
|
||||
<ArtCard
|
||||
image={props.attributes.image}
|
||||
category={props.attributes.category}
|
||||
name={props.attributes.name}
|
||||
symbol={props.attributes.symbol}
|
||||
small={true}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
<Col className="section" xl={12}>
|
||||
<label className="action-field">
|
||||
<span className="field-title">Royalty Percentage</span>
|
||||
<InputNumber
|
||||
autoFocus
|
||||
min={0}
|
||||
max={100}
|
||||
placeholder="Between 0 and 100"
|
||||
onChange={(val: number) => {
|
||||
props.setAttributes({ ...props.attributes, royalty: val });
|
||||
}}
|
||||
className="royalties-input"
|
||||
/>
|
||||
</label>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={props.confirm}
|
||||
className="action-btn"
|
||||
>
|
||||
Continue to review
|
||||
</Button>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const LaunchStep = (props: {
|
||||
confirm: () => void;
|
||||
attributes: IMetadataExtension;
|
||||
attributes: AuctionState;
|
||||
connection: Connection;
|
||||
}) => {
|
||||
const files = props.attributes.files || [];
|
||||
const metadata = {
|
||||
...(props.attributes as any),
|
||||
files: files.map(f => f?.name),
|
||||
};
|
||||
const [cost, setCost] = useState(0);
|
||||
useEffect(() => {
|
||||
const rentCall = Promise.all([
|
||||
|
@ -457,46 +416,27 @@ const LaunchStep = (props: {
|
|||
)
|
||||
]);
|
||||
|
||||
getAssetCostToStore([
|
||||
...files,
|
||||
new File([JSON.stringify(metadata)], 'metadata.json'),
|
||||
]).then(async lamports => {
|
||||
const sol = lamports / LAMPORT_MULTIPLIER;
|
||||
// TODO: add
|
||||
}, [setCost]);
|
||||
|
||||
// TODO: cache this and batch in one call
|
||||
const [mintRent, metadataRent, nameSymbolRent] = await rentCall;
|
||||
|
||||
const uriStr = 'x';
|
||||
let uriBuilder = '';
|
||||
for (let i = 0; i < MAX_URI_LENGTH; i++) {
|
||||
uriBuilder += uriStr;
|
||||
}
|
||||
|
||||
const additionalSol =
|
||||
(metadataRent + nameSymbolRent + mintRent) / LAMPORT_MULTIPLIER;
|
||||
|
||||
// TODO: add fees based on number of transactions and signers
|
||||
setCost(sol + additionalSol);
|
||||
});
|
||||
}, [...files, setCost]);
|
||||
let item = props.attributes.items?.[0];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row className="call-to-action">
|
||||
<h2>Launch your creation</h2>
|
||||
<h2>Review and list</h2>
|
||||
<p>
|
||||
Provide detailed description of your creative process to engage with
|
||||
your audience.
|
||||
Review your listing before publishing.
|
||||
</p>
|
||||
</Row>
|
||||
<Row className="content-action">
|
||||
<Col xl={12}>
|
||||
{props.attributes.image && (
|
||||
{item?.info && (
|
||||
<ArtCard
|
||||
image={props.attributes.image}
|
||||
category={props.attributes.category}
|
||||
name={props.attributes.name}
|
||||
symbol={props.attributes.symbol}
|
||||
image={item.info.extended?.image}
|
||||
category={item.info.extended?.category}
|
||||
name={item.info.name}
|
||||
symbol={item.info.symbol}
|
||||
small={true}
|
||||
/>
|
||||
)}
|
||||
|
@ -504,9 +444,8 @@ const LaunchStep = (props: {
|
|||
<Col className="section" xl={12}>
|
||||
<Statistic
|
||||
className="create-statistic"
|
||||
title="Royalty Percentage"
|
||||
value={props.attributes.royalty}
|
||||
suffix="%"
|
||||
title="Copies"
|
||||
value={props.attributes.editions === undefined ? 'Unique' : props.attributes.editions }
|
||||
/>
|
||||
{cost ? (
|
||||
<Statistic
|
||||
|
@ -527,15 +466,7 @@ const LaunchStep = (props: {
|
|||
onClick={props.confirm}
|
||||
className="action-btn"
|
||||
>
|
||||
Pay with SOL
|
||||
</Button>
|
||||
<Button
|
||||
disabled={true}
|
||||
size="large"
|
||||
onClick={props.confirm}
|
||||
className="action-btn"
|
||||
>
|
||||
Pay with Credit Card
|
||||
Publish Auction
|
||||
</Button>
|
||||
</Row>
|
||||
</>
|
||||
|
@ -543,14 +474,14 @@ const LaunchStep = (props: {
|
|||
};
|
||||
|
||||
const WaitingStep = (props: {
|
||||
mint: Function,
|
||||
createAuction: () => Promise<void>,
|
||||
progress: number,
|
||||
confirm: Function,
|
||||
confirm: () => void,
|
||||
}) => {
|
||||
|
||||
useEffect(() => {
|
||||
const func = async () => {
|
||||
await props.mint()
|
||||
await props.createAuction()
|
||||
props.confirm()
|
||||
}
|
||||
func()
|
||||
|
@ -563,9 +494,9 @@ const WaitingStep = (props: {
|
|||
percent={props.progress}
|
||||
/>
|
||||
<div className="waiting-title">
|
||||
Your creation is being uploaded to the decentralized web...
|
||||
Your creation is being listed with Metaplex...
|
||||
</div>
|
||||
<div className="waiting-subtitle">This can take up to 1 minute.</div>
|
||||
<div className="waiting-subtitle">This can take up to 30 seconds.</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -574,7 +505,7 @@ const Congrats = () => {
|
|||
return <>
|
||||
<div style={{ marginTop: 70 }}>
|
||||
<div className="waiting-title">
|
||||
Congratulations! Your creation is now live.
|
||||
Congratulations! Your auction is now live.
|
||||
</div>
|
||||
<div className="congrats-button-container">
|
||||
<Button className="congrats-button"><span>Share it on Twitter</span><span>></span></Button>
|
||||
|
|
|
@ -166,6 +166,7 @@
|
|||
.action-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
|
||||
.field-title {
|
||||
text-align: left;
|
||||
|
@ -185,9 +186,29 @@
|
|||
margin: 12px 0px;
|
||||
}
|
||||
|
||||
.field-info {
|
||||
text-align: left;
|
||||
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.field-date {
|
||||
background: #282828;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
border-width: 0px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.selected-card {
|
||||
border-width: 3px;
|
||||
border-color: #5870EE !important;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.royalties-input {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
|
|
Loading…
Reference in New Issue