mirror of https://github.com/certusone/oyster.git
feat: add art selector
This commit is contained in:
parent
a58a3baf39
commit
0fcbf48718
|
@ -416,11 +416,11 @@ export function AccountsProvider({ children = null as any }) {
|
|||
if (!connection || !publicKey) {
|
||||
setTokenAccounts([]);
|
||||
} else {
|
||||
// precacheUserTokenAccounts(connection, LEND_HOST_FEE_ADDRESS);
|
||||
precacheUserTokenAccounts(connection, LEND_HOST_FEE_ADDRESS);
|
||||
|
||||
// precacheUserTokenAccounts(connection, publicKey).then(() => {
|
||||
// setTokenAccounts(selectUserAccounts());
|
||||
// });
|
||||
precacheUserTokenAccounts(connection, publicKey).then(() => {
|
||||
setTokenAccounts(selectUserAccounts());
|
||||
});
|
||||
|
||||
// This can return different types of accounts: token-account, mint, multisig
|
||||
// TODO: web3.js expose ability to filter.
|
||||
|
|
|
@ -53,6 +53,31 @@ code {
|
|||
}
|
||||
}
|
||||
|
||||
.ant-card-cover {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card-close-button {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
z-index: 1;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 30px;
|
||||
background-color: #434343 !important;
|
||||
|
||||
:hover {
|
||||
background-color: #434343;
|
||||
}
|
||||
}
|
||||
|
||||
.text-center {
|
||||
align-self: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.ant-tabs-tab-active {
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useLayoutEffect, useState } from 'react';
|
||||
import { Card, Avatar, CardProps } from 'antd';
|
||||
import { Card, Avatar, CardProps, Button } from 'antd';
|
||||
import { MetadataCategory } from '@oyster/common';
|
||||
import { ArtContent } from './../ArtContent';
|
||||
import './index.less';
|
||||
|
@ -15,15 +15,23 @@ export interface ArtCardProps extends CardProps {
|
|||
artist?: string;
|
||||
preview?: boolean;
|
||||
small?: boolean;
|
||||
close?: () => void;
|
||||
}
|
||||
|
||||
export const ArtCard = (props: ArtCardProps) => {
|
||||
const { className, small, category, image, name, preview, artist, ...rest } = props;
|
||||
const { className, small, category, image, name, preview, artist, close, ...rest } = props;
|
||||
return (
|
||||
<Card
|
||||
hoverable={true}
|
||||
className={`art-card ${small ? 'small' : ''} ${className}`}
|
||||
cover={<ArtContent category={category} content={image} preview={preview} />}
|
||||
cover={<>
|
||||
{close && <Button className="card-close-button" shape="circle" onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
close();
|
||||
}} >X</Button>}
|
||||
<ArtContent category={category} content={image} preview={preview} />
|
||||
</>}
|
||||
{...rest}
|
||||
>
|
||||
<Meta
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Row,
|
||||
Button,
|
||||
|
@ -25,15 +25,32 @@ export const ArtSelector = (props: ArtSelectorProps) => {
|
|||
const items = useUserArts();
|
||||
const [selectedItems, setSelectedItems] = useState<Set<string>>(new Set(props.selected.map(item => item.pubkey.toBase58())));
|
||||
|
||||
const map = useMemo(() => items.reduce((acc, item) => {
|
||||
acc.set(item.pubkey.toBase58(), item.info);
|
||||
return acc;
|
||||
}, new Map<string, Metadata>()), [items]);
|
||||
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const showModal = () => {
|
||||
const open = () => {
|
||||
clear();
|
||||
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
props.setSelected(items.filter(item => selectedItems.has(item.pubkey.toBase58())));
|
||||
}, [selectedItems]);
|
||||
const close = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
setSelectedItems(new Set());
|
||||
};
|
||||
|
||||
const confirm = () => {
|
||||
let list = items.filter(item => selectedItems.has(item.pubkey.toBase58()))
|
||||
setSelected(list);
|
||||
close();
|
||||
}
|
||||
|
||||
const breakpointColumnsObj = {
|
||||
default: 4,
|
||||
|
@ -44,8 +61,37 @@ export const ArtSelector = (props: ArtSelectorProps) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Button {...rest} onClick={() => showModal} />
|
||||
<Modal visible={visible}>
|
||||
<Masonry
|
||||
breakpointCols={breakpointColumnsObj}
|
||||
className="my-masonry-grid"
|
||||
columnClassName="my-masonry-grid_column"
|
||||
>
|
||||
{[...selectedItems.keys()].map(m => {
|
||||
const item = map.get(m);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
return <ArtCard key={m}
|
||||
image={item.extended?.image}
|
||||
category={item.extended?.category}
|
||||
name={item?.name}
|
||||
symbol={item.symbol}
|
||||
preview={false}
|
||||
onClick={open}
|
||||
close={() => {
|
||||
setSelectedItems(new Set([...selectedItems.keys()].filter(_ => _ !== m)));
|
||||
confirm();
|
||||
}}
|
||||
/>;
|
||||
})}
|
||||
{(allowMultiple || selectedItems.size === 0) && <div className="ant-card ant-card-bordered ant-card-hoverable art-card" style={{ width: 200, height: 300, display: 'flex' }} onClick={open} >
|
||||
<span className="text-center">Add an NFT</span>
|
||||
</div>}
|
||||
</Masonry>
|
||||
|
||||
|
||||
<Modal visible={visible} onCancel={close} onOk={confirm} width="100" footer={null}>
|
||||
<Row className="call-to-action" style={{ marginBottom: 0 }}>
|
||||
<h2>Select the NFT you want to sell</h2>
|
||||
<p style={{ fontSize: '1.2rem' }}>
|
||||
|
@ -64,13 +110,17 @@ export const ArtSelector = (props: ArtSelectorProps) => {
|
|||
|
||||
const onSelect = () => {
|
||||
let list = [...selectedItems.keys()];
|
||||
if (props.allowMultiple) {
|
||||
if (allowMultiple) {
|
||||
list = [];
|
||||
}
|
||||
|
||||
isSelected ?
|
||||
setSelectedItems(new Set(list.filter(item => item !== id))) :
|
||||
setSelectedItems(new Set([...list, id]));
|
||||
|
||||
if(!allowMultiple) {
|
||||
confirm();
|
||||
}
|
||||
};
|
||||
|
||||
return <ArtCard key={id}
|
||||
|
@ -89,9 +139,7 @@ export const ArtSelector = (props: ArtSelectorProps) => {
|
|||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
// TODO;
|
||||
}}
|
||||
onClick={confirm}
|
||||
className="action-btn"
|
||||
>
|
||||
Confirm
|
||||
|
|
|
@ -242,9 +242,9 @@ export const AuctionCreateView = () => {
|
|||
.map(step => <Step title={step[0]} />)}
|
||||
</Steps>
|
||||
</Col>}
|
||||
<Col {...(saving ? { xl: 24 } : { xl: 16 })}>
|
||||
<Col {...(saving ? { xl: 24 } : { xl: 16, md: 17 })}>
|
||||
{stepsByCategory[attributes.category][step][1]}
|
||||
{(0 < step && step < stepsByCategory[attributes.category].length) && <Button onClick={() => gotoNextStep(step - 1)}>Back</Button>}
|
||||
{(0 < step && step < stepsByCategory[attributes.category].length) && <Button style={{ width: '100%' }} onClick={() => gotoNextStep(step - 1)}>Back</Button>}
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
|
@ -327,18 +327,10 @@ const CopiesStep = (props: {
|
|||
useEffect(() => {
|
||||
props.setAttributes({
|
||||
...props.attributes,
|
||||
// TODO: add items
|
||||
items: items.filter(item => selectedItems.has(item.pubkey.toBase58()))
|
||||
})
|
||||
}, [selectedItems]);
|
||||
|
||||
const breakpointColumnsObj = {
|
||||
default: 4,
|
||||
1100: 3,
|
||||
700: 2,
|
||||
500: 1
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row className="call-to-action" style={{ marginBottom: 0 }}>
|
||||
|
@ -348,8 +340,11 @@ const CopiesStep = (props: {
|
|||
</p>
|
||||
</Row>
|
||||
<Row className="content-action">
|
||||
<Col>
|
||||
<ArtSelector selected={[]} setSelected={() => { }} allowMultiple={false}>Select NFT</ArtSelector>
|
||||
<Col xl={24}>
|
||||
<ArtSelector
|
||||
selected={items.filter(item => selectedItems.has(item.pubkey.toBase58()))}
|
||||
setSelected={(items) => setSelectedItems(new Set(items.map(item => item.pubkey.toBase58())))}
|
||||
allowMultiple={false}>Select NFT</ArtSelector>
|
||||
<label className="action-field">
|
||||
<span className="field-title">How many copies do you want to create?</span>
|
||||
<span className="field-info">Each copy will be given unique edition number e.g. 1 of 30</span>
|
||||
|
|
Loading…
Reference in New Issue