diff --git a/minter/package.json b/minter/package.json index 22ff499..c24bf8a 100644 --- a/minter/package.json +++ b/minter/package.json @@ -17,7 +17,7 @@ "web3": "^1.6.0" }, "scripts": { - "start": "react-scripts start", + "start": "DISABLE_ESLINT_PLUGIN=true react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" diff --git a/minter/src/App.js b/minter/src/App.js index 8f459f4..4269fd5 100644 --- a/minter/src/App.js +++ b/minter/src/App.js @@ -1,4 +1,4 @@ -import { Container } from './components/generic' +import Container from './components/atoms/Container' import { useState, useEffect } from 'react' import { HashRouter} from 'react-router-dom' import Router from './components/router' @@ -24,16 +24,11 @@ function App() { // /////////////////////////////// // Rendering // /////////////////////////////// - if( error || loading ) return - -

{ error || loading }

-
-
return - + { error || loading ?

{ error || loading }

: }
diff --git a/minter/src/assets/account-circle-fill.svg b/minter/src/assets/account-circle-fill.svg new file mode 100644 index 0000000..278bbdd --- /dev/null +++ b/minter/src/assets/account-circle-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/minter/src/assets/account-circle-line.svg b/minter/src/assets/account-circle-line.svg new file mode 100644 index 0000000..5a9faf0 --- /dev/null +++ b/minter/src/assets/account-circle-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/minter/src/assets/discord-logo-black.svg b/minter/src/assets/discord-logo-black.svg new file mode 100644 index 0000000..9da6ee0 --- /dev/null +++ b/minter/src/assets/discord-logo-black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/minter/src/assets/door-closed-fill.svg b/minter/src/assets/door-closed-fill.svg new file mode 100644 index 0000000..58e9203 --- /dev/null +++ b/minter/src/assets/door-closed-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/minter/src/assets/hand-coin-fill.svg b/minter/src/assets/hand-coin-fill.svg new file mode 100644 index 0000000..71cea0c --- /dev/null +++ b/minter/src/assets/hand-coin-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/minter/src/assets/home-2-line.svg b/minter/src/assets/home-2-line.svg new file mode 100644 index 0000000..7cae93e --- /dev/null +++ b/minter/src/assets/home-2-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/minter/src/assets/pie-chart-fill.svg b/minter/src/assets/pie-chart-fill.svg new file mode 100644 index 0000000..083faf1 --- /dev/null +++ b/minter/src/assets/pie-chart-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/minter/src/assets/rocket-fill.svg b/minter/src/assets/rocket-fill.svg new file mode 100644 index 0000000..aca5be2 --- /dev/null +++ b/minter/src/assets/rocket-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/minter/src/components/atoms/Button.js b/minter/src/components/atoms/Button.js index 963d943..dc54c3d 100644 --- a/minter/src/components/atoms/Button.js +++ b/minter/src/components/atoms/Button.js @@ -1,18 +1,34 @@ import { Link } from 'react-router-dom' import styled from 'styled-components' -const DynamicButton = ( { to, ...props } ) => to ? : + + + + + @@ -80,11 +90,10 @@ export default function ComponentName( ) { // Login interface return -

Rocketeer Interface

- - metamask fox +

Rocketeer Interface

+
diff --git a/minter/src/components/minter.js b/minter/src/components/organisms/Minter.js similarity index 66% rename from minter/src/components/minter.js rename to minter/src/components/organisms/Minter.js index 18a2772..ceb9170 100644 --- a/minter/src/components/minter.js +++ b/minter/src/components/organisms/Minter.js @@ -1,11 +1,15 @@ -import Fox from '../assets/metamask-fox.svg' -import { Container } from './generic' -import '../App.css' +import Fox from '../../assets/metamask-fox.svg' + +import Container from '../atoms/Container' +import { H1, Text } from '../atoms/Text' +import Button from '../atoms/Button' +import Input from '../molecules/Input' +import Loading from '../molecules/Loading' import { useState, useEffect } from 'react' -import { useAddress, useTotalSupply, useContract, useChainId } from '../modules/web3' -import { log, setListenerAndReturnUnlistener } from '../modules/helpers' +import { useAddress, useTotalSupply, useContract, useChainId } from '../../modules/web3' +import { log, setListenerAndReturnUnlistener } from '../../modules/helpers' export default function Minter() { @@ -78,21 +82,14 @@ export default function Minter() { // Rendering // /////////////////////////////// - if( error || loading ) return - { error &&

{ error }

} - { !error && loading &&
- -
-

{ loading }

- { txHash && View tx on Etherscan } - -
} -
+ if( error || loading ) return + { txHash && } + if( mintedTokenId ) return -

Minting Successful!

- View your Rocketeers +

Minting Successful!

+
@@ -100,15 +97,15 @@ export default function Minter() { return ( -

Rocketeer Minter

-

We are ready to mint! There are currently { totalSupply } minted Rocketeers.

+

Rocketeer Minter

+ We are ready to mint! There are currently { totalSupply } minted Rocketeers. - - - { contract && - metamask fox + + + + { contract && }
diff --git a/minter/src/components/organisms/Outfits.js b/minter/src/components/organisms/Outfits.js index 9c78fda..9423df5 100644 --- a/minter/src/components/organisms/Outfits.js +++ b/minter/src/components/organisms/Outfits.js @@ -1,4 +1,3 @@ - import { useState, useEffect } from 'react' import { useRocketeers, callApi } from '../../modules/api' import { useChainId, useAddress, sign } from '../../modules/web3' @@ -164,7 +163,7 @@ export default function Verifier() { if( !rocketeers.length || loading ) return // Rocketeer selector - if(!rocketeer ) return + if(!rocketeer ) return

Rocketeers

Click on a Rocketeer to manage it's outfits @@ -184,7 +183,7 @@ export default function Verifier() {
// Changing room - if( rocketeer ) return + if( rocketeer ) return { /* Header */ } diff --git a/minter/src/components/organisms/Portfolio.js b/minter/src/components/organisms/Portfolio.js new file mode 100644 index 0000000..cbdd700 --- /dev/null +++ b/minter/src/components/organisms/Portfolio.js @@ -0,0 +1,52 @@ +import Container from '../atoms/Container' +import Section from '../atoms/Section' +import { H1, Text } from '../atoms/Text' +import Avatar from '../molecules/Avatar' +import Loading from '../molecules/Loading' + +import { useState, useEffect } from 'react' +import { useRocketeerImages } from '../../modules/api' +import { useAddress } from '../../modules/web3' + + +export default function Verifier() { + + // /////////////////////////////// + // State management + // /////////////////////////////// + const address = useAddress() + const metamaskAddress = useAddress() + const [ validatorAddress, setValidatorAddress ] = useState( ) + const rocketeers = useRocketeerImages() + + + // /////////////////////////////// + // Lifecycle + // /////////////////////////////// + useEffect( f => { + if( !validatorAddress && metamaskAddress ) setValidatorAddress( metamaskAddress ) + }, [ metamaskAddress, validatorAddress ] ) + + // /////////////////////////////// + // Rendering + // /////////////////////////////// + if( !address && !rocketeers.length ) return + return + +

Portfolio

+ Click a Rocketeer to view it's details. +
+ + { rocketeers.map( ( { id, src } ) => { + + return window.location.href =`https://viewer.rocketeer.fans/?rocketeer=${ id }` } key={ id } src={ src } alt={ `Rocketeer number ${ id }` } /> + + } ) } + + Rocketeers owned by: { address }. + + +
+ +
+} \ No newline at end of file diff --git a/minter/src/components/verifier.js b/minter/src/components/organisms/Verifier.js similarity index 67% rename from minter/src/components/verifier.js rename to minter/src/components/organisms/Verifier.js index 5706533..14aefec 100644 --- a/minter/src/components/verifier.js +++ b/minter/src/components/organisms/Verifier.js @@ -1,9 +1,11 @@ -import { Container } from './generic' -import '../App.css' +import Container from '../atoms/Container' +import { H1, Text } from '../atoms/Text' +import Button from '../atoms/Button' +import Input from '../molecules/Input' import { useState, useEffect } from 'react' -import { log } from '../modules/helpers' -import { useAddress, useChainId, useBalanceOf } from '../modules/web3' +import { log } from '../../modules/helpers' +import { useAddress, useChainId, useBalanceOf } from '../../modules/web3' import { useParams } from 'react-router-dom' export default function Verifier() { @@ -83,29 +85,29 @@ export default function Verifier() { // /////////////////////////////// log('Rendering with ', message, verifyUrl ) if( message ) return - { message.balance > 0 &&

✅ { message.username } has { message.balance } Rocketeers on chain { chainId }

} - { message.balance < 1 &&

🛑 Computer says no

} - { error &&

Something went wrong, contact #support in Discord

} + { message.balance > 0 && ✅ { message.username } has { message.balance } Rocketeers on chain { chainId } } + { message.balance < 1 && 🛑 Computer says no } + { error && Something went wrong, contact #support in Discord }
if( verifyUrl ) return - { !balance &&

Checking your on-chain balance...

} + { !balance && Checking your on-chain balance... } { balance && <> -

Verification URL

-

Post this in the Discord channel #get-verified:

-

{ verifyUrl }

+

Verification URL

+ Post this in the Discord channel #get-verified: + { verifyUrl } }
return -

Verify your hodlr status

-

Verify your Rocketeer status by logging in with your wallet. This does NOT trigger a transaction. Therefore it is free.

- setUsername( e.target.value ) } type="text" placeholder="Your Discord username" /> - Verify +

Verify your hodlr status

+ Verify your Rocketeer status by logging in with your wallet. This does NOT trigger a transaction. Therefore it is free. + setUsername( e.target.value ) } type="text" placeholder="Your Discord username"/> +
} \ No newline at end of file diff --git a/minter/src/components/outfits.js b/minter/src/components/outfits.js deleted file mode 100644 index 43a8341..0000000 --- a/minter/src/components/outfits.js +++ /dev/null @@ -1,198 +0,0 @@ -import { Container, Loading } from './generic' -import '../App.css' - -import { useState, useEffect } from 'react' -import { useRocketeers, callApi } from '../modules/api' -import { useChainId, useAddress, sign } from '../modules/web3' -import { log } from '../modules/helpers' -import { useParams, useNavigate } from 'react-router' - - -export default function Verifier() { - - const { rocketeerId } = useParams() - const navigate = useNavigate() - - // /////////////////////////////// - // State management - // /////////////////////////////// - const address = useAddress() - const metamaskAddress = useAddress() - const [ validatorAddress, setValidatorAddress ] = useState( ) - const rocketeers = useRocketeers( rocketeerId ) - const chainId = useChainId() - const [ rocketeer, setRocketeer ] = useState( ) - const [ loading, setLoading ] = useState( ) - - - /* /////////////////////////////// - // Functions - // /////////////////////////////*/ - async function setPrimaryOutfit( outfitId ) { - - try { - - log( `Setting outfit ${ outfitId } for Rocketeer #${ rocketeerId }` ) - setLoading( `Setting outfit ${ outfitId } for Rocketeer #${ rocketeerId }` ) - alert( 'You will be prompted to sign a message, this is NOT a transaction' ) - - const signature = await sign( JSON.stringify( { - signer: address.toLowerCase(), - outfitId, - chainId, - } ), address ) - - log( 'Making request with ', signature ) - - setLoading( 'Updating profile' ) - - const { error, success } = await callApi( `/rocketeer/${ rocketeerId }/outfits`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify( signature ) - } ) - - if( error ) throw new Error( error ) - - alert( `Success! Outfit changed, please click "refresh metadata" on Opensea to update it there.\nForwarding you to the tools homepage.` ) - navigate( `/` ) - - } catch( e ) { - - log( e ) - alert( e.message ) - - } finally { - - setLoading( false ) - - } - - } - - async function generateNewOutfit( ) { - - try { - - log( `Generating new outfit for #${ rocketeerId }` ) - setLoading( `Generating new outfit for #${ rocketeerId }` ) - alert( 'You will be prompted to sign a message, this is NOT a transaction' ) - - const signature = await sign( JSON.stringify( { - signer: address.toLowerCase(), - rocketeerId, - chainId, - } ), address ) - - log( 'Making request with ', signature ) - - setLoading( 'Generating new outfit, this can take a minute' ) - - const { error, success } = await callApi( `/rocketeer/${ rocketeerId }/outfits`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify( signature ) - } ) - - if( error ) throw new Error( error ) - - alert( `Success! Outfit generated.` ) - window?.location.reload() - - - } catch( e ) { - - log( e ) - alert( e.message ) - - } finally { - - setLoading( false ) - - } - - } - - // /////////////////////////////// - // Lifecycle - // /////////////////////////////// - useEffect( f => { - if( !validatorAddress && metamaskAddress ) setValidatorAddress( metamaskAddress ) - }, [ metamaskAddress, validatorAddress ] ) - - useEffect( f => { - - // Find the data for the clicked Rocketeer - const selected = rocketeers.find( ( { id } ) => id === rocketeerId ) - - // If the selected rocketeer is available, compute it's available outfits to an easy to access property - if( selected ) { - - const newOutfitAllowedInterval = 1000 * 60 * 60 * 24 * 30 - const { value: outfits } = selected.attributes.find( ( { trait_type } ) => trait_type === 'available outfits' ) || { value: 0 } - const { value: last_outfit_change } = selected.attributes.find( ( { trait_type } ) => trait_type === 'last outfit change' ) || { value: 0 } - const timeUntilAllowedToChange = newOutfitAllowedInterval - ( Date.now() - last_outfit_change ) - - selected.outfits = outfits - selected.last_outfit_change = last_outfit_change - selected.new_outfit_available = timeUntilAllowedToChange < 0 - selected.when_new_outfit = new Date( Date.now() + timeUntilAllowedToChange ) - } - - log( "Selecting rocketeer ", selected ) - - // Set the selected rocketeer to state - if( selected ) setRocketeer( selected ) - - }, [ rocketeerId, rocketeers.length ] ) - - // /////////////////////////////// - // Rendering - // /////////////////////////////// - - if( !rocketeers.length || loading ) return - - // Rocketeer selector - if(!rocketeer ) return 1 ? 'wide' : '' }> - -

Rocketeers

-

Click on a Rocketeer to manage it's outfits

-
- - { rocketeers.map( ( { id, image } ) => { - - return navigate( `/outfits/${ id }` ) } key={ id } className='rocketeer' src={ image } alt={ `Rocketeer number ${ id }` } /> - - } ) } - -

Rocketeers owned by: { address }.

- - -
- -
- - // Changing room - if( rocketeer ) return 0 ? 'wide' : '' }> - -

{ rocketeer.name }

- - { - { rocketeer.new_outfit_available ? :

New outfit available on { rocketeer.when_new_outfit.toString() }

} -

This Rocketeer has { 1 + rocketeer.outfits } outfits. { rocketeer.outfits > 0 && 'Click any outfit to select it as primary.' }

- -
- - setPrimaryOutfit( 0 ) } className='rocketeer' src={ rocketeer.image.replace( /-\d\.jpg/, '.jpg' ) } alt={ `Rocketeer number ${ rocketeer.id }` } /> - - { Array.from( Array( rocketeer.outfits ) ).map( ( val, i ) => { - return setPrimaryOutfit( i + 1 ) } key={ rocketeer.id + i } className='rocketeer' src={ rocketeer.image.replace( /-\d\.jpg/, `-${ i + 1 }.jpg` ) } alt={ `Rocketeer number ${ rocketeer.id }` } /> - } ) } - -
- -

Rocketeers owned by: { address }.

- -
- -} \ No newline at end of file diff --git a/minter/src/components/portfolio.js b/minter/src/components/portfolio.js deleted file mode 100644 index b8496e6..0000000 --- a/minter/src/components/portfolio.js +++ /dev/null @@ -1,48 +0,0 @@ -import { Container } from './generic' -import '../App.css' - -import { useState, useEffect } from 'react' -import { useRocketeerImages } from '../modules/api' -import { useAddress } from '../modules/web3' - - -export default function Verifier() { - - // /////////////////////////////// - // State management - // /////////////////////////////// - const address = useAddress() - const metamaskAddress = useAddress() - const [ validatorAddress, setValidatorAddress ] = useState( ) - const rocketeers = useRocketeerImages() - - - // /////////////////////////////// - // Lifecycle - // /////////////////////////////// - useEffect( f => { - if( !validatorAddress && metamaskAddress ) setValidatorAddress( metamaskAddress ) - }, [ metamaskAddress, validatorAddress ] ) - - // /////////////////////////////// - // Rendering - // /////////////////////////////// - return 1 ? 'wide' : '' }> - -

Portfolio

-

Click a Rocketeer to view it's details.

-
- - { rocketeers.map( ( { id, src } ) => { - - return window.location.href =`https://viewer.rocketeer.fans/?rocketeer=${ id }` } key={ id } className='rocketeer' src={ src } alt={ `Rocketeer number ${ id }` } /> - - } ) } - -

Rocketeers owned by: { address }.

- - -
- -
-} \ No newline at end of file diff --git a/minter/src/components/router.js b/minter/src/components/router.js index ea6c774..e874a3c 100644 --- a/minter/src/components/router.js +++ b/minter/src/components/router.js @@ -1,8 +1,8 @@ -import Minter from './minter' -import Metamask from './metamask' -import Verifier from './verifier' -import Avatar from './avatar' -import Portfolio from './portfolio' +import Minter from './organisms/Minter' +import Home from './organisms/Home' +import Verifier from './organisms/Verifier' +import Avatar from './organisms/Avatar' +import Portfolio from './organisms/Portfolio' import Outfits from './organisms/Outfits' import { useState, useEffect } from 'react' import { log } from '../modules/helpers' @@ -46,7 +46,7 @@ function Router() { // /////////////////////////////// return - } /> + } /> } /> }> } />