mirror of https://github.com/certusone/oyster.git
feat: routes
This commit is contained in:
parent
f9bfa0fd36
commit
f1c08ebcdb
|
@ -1,2 +1,2 @@
|
|||
export * from './market';
|
||||
export * from './accounts';
|
||||
export * from './meta';
|
||||
|
|
|
@ -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:";
|
||||
}
|
|
@ -1 +1 @@
|
|||
export const nop = () => {};
|
||||
export * from './useArt';
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
export const useNFTs = () => {
|
||||
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue