feat: routes

This commit is contained in:
bartosz-lipinski 2021-04-05 14:16:25 -05:00
parent f9bfa0fd36
commit f1c08ebcdb
7 changed files with 126 additions and 13 deletions

View File

@ -1,2 +1,2 @@
export * from './market';
export * from './accounts';
export * from './meta';

View File

@ -0,0 +1,94 @@
import { EventEmitter, programIds, useConnection, decodeMetadata, Metadata, getMultipleAccounts, cache, MintParser, ParsedAccount } from '@oyster/common';
import { MintInfo } from '@solana/spl-token';
import BN from 'bn.js';
import React, { useContext, useEffect, useState } from 'react';
export interface MetaContextState {
accounts: ParsedAccount<Metadata>[];
}
const MetaContext = React.createContext<MetaContextState>(
{
accounts: []
},
);
export function MetaProvider({ children = null as any }) {
const connection = useConnection();
const [accounts, setMetaAccounts] = useState<ParsedAccount<Metadata>[]>([]);
useEffect(() => {
(async () => {
const accounts = await connection.getProgramAccounts(programIds().metadata);
const mintToMetadata = new Map<string, ParsedAccount<Metadata>>();
const extendedMetadataFetch = new Map<string, Promise<any>>();
accounts.forEach(meta => {
try{
const metadata = decodeMetadata(meta.account.data);
if(isValidHttpUrl(metadata.uri)) {
const account: ParsedAccount<Metadata> = {
pubkey: meta.pubkey,
account: meta.account,
info: metadata,
};
mintToMetadata.set(metadata.mint.toBase58(), account);
}
} catch {
// ignore errors
// add type as first byte for easier deserialization
}
});
const mints = await getMultipleAccounts(connection, [...mintToMetadata.keys()], 'single');
mints.keys.forEach((key, index) => {
const mintAccount = mints.array[index];
const mint = cache.add(key, mintAccount, MintParser) as ParsedAccount<MintInfo>;
if(mint.info.supply.gt(new BN(1)) || mint.info.decimals !== 0) {
// naive not NFT check
mintToMetadata.delete(key);
} else {
const metadata = mintToMetadata.get(key);
if(metadata && metadata.info.uri) {
extendedMetadataFetch.set(key, fetch(metadata.info.uri).catch(() => {
mintToMetadata.delete(key);
return undefined;
}).then(_ => {
metadata.info.extended = _;
}));
}
}
});
Promise.all([...extendedMetadataFetch.values()]);
setMetaAccounts([...mintToMetadata.values()]);
console.log([...mintToMetadata.values()]);
})();
}, [connection, setMetaAccounts])
return (
<MetaContext.Provider value={{ accounts }}>
{children}
</MetaContext.Provider>
);
}
export const useMeta = () => {
const context = useContext(MetaContext);
return context as MetaContextState;
};
function isValidHttpUrl(text: string) {
let url;
try {
url = new URL(text);
} catch (_) {
return false;
}
return url.protocol === "http:" || url.protocol === "https:";
}

View File

@ -1 +1 @@
export const nop = () => {};
export * from './useArt';

View File

@ -0,0 +1,12 @@
import React, { useMemo } from 'react';
import { PublicKey } from '@solana/web3.js';
import { useMeta } from './../contexts';
export const useArt = (id: PublicKey | string) => {
const { accounts } = useMeta();
const key = typeof id === 'string' ? id : (id?.toBase58() || '');
const account = useMemo(() => accounts.find(a => a.pubkey.toBase58() === key), [key, accounts]);
return account;
}

View File

@ -1,5 +0,0 @@
import React from 'react';
export const useNFTs = () => {
}

View File

@ -3,7 +3,7 @@ import React from 'react';
import { contexts } from '@oyster/common';
import {
MarketProvider,
VinciAccountsProvider,
MetaProvider,
} from './contexts';
import { AppLayout } from './components/Layout';
@ -22,7 +22,7 @@ export function Routes() {
<UseWalletProvider chainId={5}>
<AccountsProvider>
<MarketProvider>
<VinciAccountsProvider>
<MetaProvider>
<AppLayout>
<Switch>
<Route
@ -67,7 +67,7 @@ export function Routes() {
/>
</Switch>
</AppLayout>
</VinciAccountsProvider>
</MetaProvider>
</MarketProvider>
</AccountsProvider>
</UseWalletProvider>

View File

@ -1,9 +1,21 @@
import React from 'react';
import { Row, Card } from 'antd';
import { useParams } from 'react-router-dom';
import { useArt } from './../../hooks';
export const ArtView = () => {
const { id } = useParams<{ id: string }>();
const art = useArt(id);
const image = `/img/auction2.jpg`
return (
<div className="flexColumn" style={{ flex: 1 }}>
TODO: ART view
</div>
<>
<Row>
<Card className="custom-card" cover={<img src={image} />}>
</Card>
</Row>
</>
);
};