Merge branch 'develop' into badge-integration

This commit is contained in:
Serkan Reis 2023-02-23 09:47:05 +03:00 committed by GitHub
commit 26a8823757
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 323 additions and 36 deletions

View File

@ -1,4 +1,4 @@
APP_VERSION=0.4.2 APP_VERSION=0.4.4
NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS
NEXT_PUBLIC_SG721_CODE_ID=793 NEXT_PUBLIC_SG721_CODE_ID=793

2
.github/CODEOWNERS vendored
View File

@ -1 +1 @@
* @findolor @MightOfOaks @name-user1 * @MightOfOaks @name-user1 @Ninjatosba

View File

@ -0,0 +1,83 @@
import { useRef, useState } from 'react'
import { Button } from './Button'
export interface IncomeDashboardDisclaimerProps {
creatorAddress: string
}
export const IncomeDashboardDisclaimer = (props: IncomeDashboardDisclaimerProps) => {
const [isChecked, setIsChecked] = useState(false)
const checkBoxRef = useRef<HTMLInputElement>(null)
const handleCheckBox = () => {
checkBoxRef.current?.click()
}
return (
<div>
<input className="modal-toggle" defaultChecked={false} id="my-modal-1" ref={checkBoxRef} type="checkbox" />
<label className="cursor-pointer modal" htmlFor="my-modal-1">
<label
className="absolute top-[25%] bottom-5 left-1/3 max-w-[600px] max-h-[450px] border-2 no-scrollbar modal-box"
htmlFor="temp"
>
{/* <Alert type="warning"></Alert> */}
<div className="text-xl font-bold">
<div className="text-sm font-thin">
The tool provided on this website is for informational purposes only and does not constitute tax, legal or
financial advice. The information provided by the tool is not intended to be used for tax planning, tax
avoidance, promoting, marketing or related purposes. Users should consult their own tax, legal or
financial advisors prior to acting on any information provided by the tool. By clicking accept below, you
agree that neither Stargaze Foundation or Public Awesome, LLC or any of its directors, officers,
employees, or advisors shall be responsible for any errors, omissions, or inaccuracies in the information
provided by the tool, and shall not be liable for any damages, losses, or expenses arising out of or in
connection with the use of the tool. Furthermore, you agree to indemnify Stargaze Foundation, Public
Awesome, LLC and any of its directors, officers, employees and advisors against any claims, suits, or
actions related to your use of the tool.
</div>
<br />
<div className="flex flex-row pb-4">
<label className="flex flex-col space-y-1" htmlFor="terms">
<span className="text-sm font-light text-white">I agree with the terms above.</span>
</label>
<input
checked={isChecked}
className="p-2 mb-1 ml-2"
id="terms"
name="terms"
onClick={() => setIsChecked(!isChecked)}
type="checkbox"
/>
</div>
<br />
Are you sure to proceed to the Creator Income Dashboard?
</div>
<div className="flex justify-end w-full">
<Button className="px-0 mt-4 mr-5 mb-4 max-h-12 bg-gray-600 hover:bg-gray-600">
<label
className="w-full h-full text-white bg-gray-600 hover:bg-gray-500 border-0 btn modal-button"
htmlFor="my-modal-1"
>
Go Back
</label>
</Button>
<a
className="my-4"
href={
isChecked
? `https://metabase.constellations.zone/public/dashboard/4d751721-51ab-46ff-ad27-075ec8d47a17?creator_address=${props.creatorAddress}&chart_granularity_(day%252Fweek%252Fmonth)=week`
: undefined
}
rel="noopener"
target="_blank"
>
<Button className="px-5 w-full h-full" isDisabled={!isChecked} onClick={() => handleCheckBox()}>
Confirm
</Button>
</a>
</div>
</label>
</label>
</div>
)
}

View File

@ -1,5 +1,6 @@
/* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */ /* eslint-disable jsx-a11y/no-noninteractive-tabindex */
import clsx from 'clsx' import clsx from 'clsx'
import { Anchor } from 'components/Anchor' import { Anchor } from 'components/Anchor'
import { useWallet } from 'contexts/wallet' import { useWallet } from 'contexts/wallet'
@ -8,6 +9,7 @@ import { useRouter } from 'next/router'
// import BrandText from 'public/brand/brand-text.svg' // import BrandText from 'public/brand/brand-text.svg'
import { footerLinks, socialsLinks } from 'utils/links' import { footerLinks, socialsLinks } from 'utils/links'
import { BADGE_HUB_ADDRESS, BASE_FACTORY_ADDRESS } from '../utils/constants' import { BADGE_HUB_ADDRESS, BASE_FACTORY_ADDRESS } from '../utils/constants'
import { Conditional } from './Conditional' import { Conditional } from './Conditional'
import { SidebarLayout } from './SidebarLayout' import { SidebarLayout } from './SidebarLayout'
@ -27,6 +29,7 @@ export const Sidebar = () => {
{/* wallet button */} {/* wallet button */}
<WalletLoader /> <WalletLoader />
{/* main navigation routes */} {/* main navigation routes */}
<div className="absolute top-[20%] left-[5%]"> <div className="absolute top-[20%] left-[5%]">
<ul className="group p-2 w-full bg-transparent menu rounded-box"> <ul className="group p-2 w-full bg-transparent menu rounded-box">
<li tabIndex={0}> <li tabIndex={0}>

View File

@ -22,7 +22,6 @@ export const ACTION_TYPES = [
'update_per_address_limit', 'update_per_address_limit',
'update_collection_info', 'update_collection_info',
'freeze_collection_info', 'freeze_collection_info',
'withdraw',
'transfer', 'transfer',
'batch_transfer', 'batch_transfer',
'burn', 'burn',
@ -60,11 +59,6 @@ export const BASE_ACTION_LIST: ActionListItem[] = [
name: 'Freeze Collection Info', name: 'Freeze Collection Info',
description: `Freeze collection info to prevent further updates`, description: `Freeze collection info to prevent further updates`,
}, },
{
id: 'withdraw',
name: 'Withdraw Tokens',
description: `Withdraw tokens from the contract`,
},
{ {
id: 'transfer', id: 'transfer',
name: 'Transfer Tokens', name: 'Transfer Tokens',
@ -153,11 +147,6 @@ export const VENDING_ACTION_LIST: ActionListItem[] = [
name: 'Freeze Collection Info', name: 'Freeze Collection Info',
description: `Freeze collection info to prevent further updates`, description: `Freeze collection info to prevent further updates`,
}, },
{
id: 'withdraw',
name: 'Withdraw Tokens',
description: `Withdraw tokens from the contract`,
},
{ {
id: 'transfer', id: 'transfer',
name: 'Transfer Tokens', name: 'Transfer Tokens',
@ -224,7 +213,6 @@ export type DispatchExecuteArgs = {
| { type: Select<'update_start_trading_time'>; startTime?: string } | { type: Select<'update_start_trading_time'>; startTime?: string }
| { type: Select<'update_per_address_limit'>; limit: number } | { type: Select<'update_per_address_limit'>; limit: number }
| { type: Select<'shuffle'> } | { type: Select<'shuffle'> }
| { type: Select<'withdraw'> }
| { type: Select<'transfer'>; recipient: string; tokenId: number } | { type: Select<'transfer'>; recipient: string; tokenId: number }
| { type: Select<'batch_transfer'>; recipient: string; tokenIds: string } | { type: Select<'batch_transfer'>; recipient: string; tokenIds: string }
| { type: Select<'burn'>; tokenId: number } | { type: Select<'burn'>; tokenId: number }
@ -284,9 +272,6 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => {
case 'shuffle': { case 'shuffle': {
return vendingMinterMessages.shuffle(txSigner) return vendingMinterMessages.shuffle(txSigner)
} }
case 'withdraw': {
return vendingMinterMessages.withdraw(txSigner)
}
case 'transfer': { case 'transfer': {
return sg721Messages.transferNft(args.recipient, args.tokenId.toString()) return sg721Messages.transferNft(args.recipient, args.tokenId.toString())
} }
@ -365,9 +350,6 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => {
case 'shuffle': { case 'shuffle': {
return vendingMinterMessages(minterContract)?.shuffle() return vendingMinterMessages(minterContract)?.shuffle()
} }
case 'withdraw': {
return vendingMinterMessages(minterContract)?.withdraw()
}
case 'transfer': { case 'transfer': {
return sg721Messages(sg721Contract)?.transferNft(args.recipient, args.tokenId.toString()) return sg721Messages(sg721Contract)?.transferNft(args.recipient, args.tokenId.toString())
} }

View File

@ -14,7 +14,6 @@ export const EXECUTE_TYPES = [
'mint_to', 'mint_to',
'mint_for', 'mint_for',
'shuffle', 'shuffle',
'withdraw',
'burn_remaining', 'burn_remaining',
] as const ] as const
@ -106,7 +105,6 @@ export type DispatchExecuteArgs = {
| { type: Select<'mint_to'>; recipient: string } | { type: Select<'mint_to'>; recipient: string }
| { type: Select<'mint_for'>; recipient: string; tokenId: number } | { type: Select<'mint_for'>; recipient: string; tokenId: number }
| { type: Select<'shuffle'> } | { type: Select<'shuffle'> }
| { type: Select<'withdraw'> }
| { type: Select<'burn_remaining'> } | { type: Select<'burn_remaining'> }
) )
@ -146,9 +144,6 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => {
case 'shuffle': { case 'shuffle': {
return messages.shuffle(txSigner) return messages.shuffle(txSigner)
} }
case 'withdraw': {
return messages.withdraw(txSigner)
}
case 'burn_remaining': { case 'burn_remaining': {
return messages.burnRemaining(txSigner) return messages.burnRemaining(txSigner)
} }
@ -193,9 +188,6 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => {
case 'shuffle': { case 'shuffle': {
return messages(contract)?.shuffle() return messages(contract)?.shuffle()
} }
case 'withdraw': {
return messages(contract)?.withdraw()
}
case 'burn_remaining': { case 'burn_remaining': {
return messages(contract)?.burnRemaining() return messages(contract)?.burnRemaining()
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "stargaze-studio", "name": "stargaze-studio",
"version": "0.4.2", "version": "0.4.4",
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
], ],

View File

@ -214,7 +214,7 @@ const CollectionCreationPage: NextPage = () => {
setCreatingCollection(false) setCreatingCollection(false)
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
toast.error(error.message, { style: { maxWidth: 'none' } }) toast.error(error.message, { style: { maxWidth: 'none' }, duration: 10000 })
setCreatingCollection(false) setCreatingCollection(false)
setUploading(false) setUploading(false)
} }
@ -276,7 +276,7 @@ const CollectionCreationPage: NextPage = () => {
setCreatingCollection(false) setCreatingCollection(false)
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
toast.error(error.message, { style: { maxWidth: 'none' } }) toast.error(error.message, { style: { maxWidth: 'none' }, duration: 10000 })
setCreatingCollection(false) setCreatingCollection(false)
setUploading(false) setUploading(false)
} }
@ -498,7 +498,7 @@ const CollectionCreationPage: NextPage = () => {
setTransactionHash(data.transactionHash) setTransactionHash(data.transactionHash)
setVendingMinterContractAddress(data.baseMinterAddress) setVendingMinterContractAddress(data.baseMinterAddress)
setSg721ContractAddress(data.sg721Address) setSg721ContractAddress(data.sg721Address)
if (uploadDetails?.assetFiles.length === 1) { if (uploadDetails?.assetFiles.length === 1 || uploadDetails?.uploadMethod === 'existing') {
await toast await toast
.promise( .promise(
baseMinterContract.use(data.baseMinterAddress)?.mint(wallet.address, baseUri) as Promise<string>, baseMinterContract.use(data.baseMinterAddress)?.mint(wallet.address, baseUri) as Promise<string>,
@ -676,9 +676,14 @@ const CollectionCreationPage: NextPage = () => {
if (!uploadDetails) { if (!uploadDetails) {
throw new Error('Please select assets and metadata') throw new Error('Please select assets and metadata')
} }
// if (minterType === 'base' && uploadDetails.uploadMethod === 'new' && uploadDetails.assetFiles.length > 1) { if (
// throw new Error('Base Minter can only mint one asset at a time. Please select only one asset.') minterType === 'base' &&
// } uploadDetails.uploadMethod === 'new' &&
uploadDetails.assetFiles.length > 1 &&
uploadDetails.metadataFiles.length === 0
) {
throw new Error('Please select metadata files')
}
if (uploadDetails.uploadMethod === 'new' && uploadDetails.assetFiles.length === 0) { if (uploadDetails.uploadMethod === 'new' && uploadDetails.assetFiles.length === 0) {
throw new Error('Please select the assets') throw new Error('Please select the assets')
} }

View File

@ -1,4 +1,5 @@
/* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
@ -56,7 +57,13 @@ const CollectionList: NextPage = () => {
<div className="w-28 h-28 mask mask-squircle"> <div className="w-28 h-28 mask mask-squircle">
<img <img
alt="Cover" alt="Cover"
src={`https://ipfs.stargaze.zone/ipfs/${(collection.image as string).substring(7)}`} src={
(collection?.image as string).startsWith('ipfs')
? `https://ipfs-gw.stargaze-apis.com/ipfs/${(collection?.image as string).substring(
7,
)}`
: collection?.image
}
/> />
</div> </div>
</div> </div>

View File

@ -0,0 +1,215 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import axios from 'axios'
import { Button } from 'components/Button'
import { ContractPageHeader } from 'components/ContractPageHeader'
import { AddressInput } from 'components/forms/FormInput'
import { useInputState } from 'components/forms/FormInput.hooks'
import type { AppConfig } from 'config'
import { getConfig } from 'config'
import type { NextPage } from 'next'
import { useRouter } from 'next/router'
import { NextSeo } from 'next-seo'
import { useEffect, useState } from 'react'
import { toast } from 'react-hot-toast'
import { NETWORK } from 'utils/constants'
import { withMetadata } from 'utils/layout'
import { links } from 'utils/links'
interface ConfigResponse {
num_tokens: number
base_token_uri: string
}
const CollectionQueriesPage: NextPage = () => {
const [client, setClient] = useState<CosmWasmClient>()
const [baseUri, SetBaseUri] = useState('')
const [doneCount, setDoneCount] = useState(0)
const [errorList, setErrorList] = useState<number[]>([])
const [numTokens, setNumTokens] = useState(0)
const [percentage, setPercentage] = useState('0.00')
const [isLoading, setIsLoading] = useState(false)
const minterContractState = useInputState({
id: 'minter-contract-address',
name: 'minter-contract-address',
title: 'Minter Contract Address',
defaultValue: '',
placeholder: 'stars1...',
})
const minterContractAddress = minterContractState.value
const router = useRouter()
useEffect(() => {
async function init() {
const config: AppConfig = getConfig(NETWORK)
setClient(await CosmWasmClient.connect(config.rpcUrl))
}
void init()
}, [])
useEffect(() => {
async function get() {
if (client && minterContractAddress) {
const res: ConfigResponse = await client.queryContractSmart(minterContractAddress, {
config: {},
})
setNumTokens(res.num_tokens)
SetBaseUri(res.base_token_uri)
setPercentage('0.00')
setDoneCount(0)
setErrorList([])
}
}
void get()
}, [minterContractAddress, client])
useEffect(() => {
if (minterContractAddress.length > 0) {
void router.replace({ query: { minterContractAddress } })
}
}, [minterContractAddress])
useEffect(() => {
const initial = new URL(document.URL).searchParams.get('minterContractAddress')
if (initial && initial.length > 0) minterContractState.onChange(initial)
}, [])
useEffect(() => {
if (doneCount === numTokens) setIsLoading(false)
if (numTokens !== 0) setPercentage(((doneCount / numTokens) * 100).toFixed(2))
else setPercentage('0.00')
}, [doneCount, numTokens])
function chooseAlternate(url: string, attempt: number) {
let alternate
switch (attempt) {
case 1: {
alternate = url.replace('ipfs://', 'https://cf-ipfs.com/ipfs/')
break
}
case 2: {
alternate = url.replace('ipfs://', 'https://ipfs.io/ipfs/')
break
}
case 3: {
alternate = url.replace('ipfs://', 'https://ipfs-gw.stargaze-apis.com/ipfs/')
break
}
default: {
alternate = url.replace('ipfs://', 'https://ipfs.stargaze.zone/ipfs/')
break
}
}
return alternate
}
async function warmOne(i: number, image: string, attempt: number, totalAttempt: number) {
const link = image.replace('1', i.toString())
try {
const result = await axios.get(chooseAlternate(link, attempt))
if (result.status === 200) {
setDoneCount((count) => {
return count + 1
})
return
}
} catch (e) {
toast.error(e as string)
}
if (totalAttempt !== 4) {
void warmOne(i, image, attempt === 3 ? 0 : attempt + 1, attempt === 3 ? totalAttempt + 1 : totalAttempt)
} else {
setErrorList((existingItems) => {
return [i, ...existingItems]
})
setDoneCount((count) => {
return count + 1
})
}
}
async function warmingProcess(url: string, attempt: number, err: boolean) {
const link = `${chooseAlternate(url, attempt)}/1`
try {
const { data, status } = await axios.get(link)
if (status === 200) {
if (!err) {
for (let i = 1; i <= numTokens; i++) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
void warmOne(i, data.image, 0, 0)
}
} else {
const list = errorList
setErrorList([])
list.forEach((i) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
void warmOne(i, data.image, 0, 0)
})
}
} else if (attempt < 3) {
void warmingProcess(url, attempt + 1, err)
} else toast.error('File can not be reachable at the moment! Please try again later...')
} catch (e) {
toast.error(e as string)
}
}
return (
<section className="py-6 px-12 space-y-4">
<NextSeo title="Collection Warm Up" />
<ContractPageHeader
description="Here you can pre-warm your collection for improved IPFS performance."
link={links.Documentation}
title="Collection Warm Up"
/>
<div className="space-y-8">
<AddressInput {...minterContractState} />
</div>
<div className="flex flex-row">
<div className="flex flex-col mr-20 w-4/5 text-xl border border-stargaze">
<div className="flex flex-row mt-2 w-full text-center">
<div className="w-1/3">Total</div>
<div className="w-1/3">Done</div>
<div className="w-1/3">Error</div>
<div className="w-1/3">Percentage</div>
</div>
<div className="flex flex-row w-full text-center">
<div className="w-1/3">{numTokens}</div>
<div className="w-1/3">{doneCount}</div>
<div className="w-1/3">{errorList.length}</div>
<div className="w-1/3">{percentage}%</div>
</div>
<div className="flex justify-center w-full">
<progress className="my-5 mx-2 w-4/5 h-2 progress" max="100" value={percentage} />
</div>
</div>
<div className="flex flex-row content-center p-10">
<Button
isDisabled={baseUri === ''}
isLoading={isLoading}
onClick={() => {
setIsLoading(true)
if (errorList.length === 0) {
setDoneCount(0)
setErrorList([])
void warmingProcess(baseUri, 0, false)
} else {
setDoneCount(doneCount - errorList.length)
void warmingProcess(baseUri, 0, true)
}
}}
>
{(numTokens === 0 || errorList.length === 0) && <span>Start</span>}
{numTokens !== 0 && errorList.length > 0 && <span>Start</span>}
</Button>
</div>
</div>
</section>
)
}
export default withMetadata(CollectionQueriesPage, { center: false })