Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
frontend
Commits
70d95e92
Commit
70d95e92
authored
May 26, 2023
by
tom
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
skeletons for token instance
parent
dd1daf82
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
294 additions
and
280 deletions
+294
-280
tokens.ts
mocks/address/tokens.ts
+9
-0
[id].tsx
pages/token/[hash]/instance/[id].tsx
+6
-2
address.ts
stubs/address.ts
+1
-1
token.ts
stubs/token.ts
+2
-4
Token.tsx
ui/pages/Token.tsx
+1
-1
TokenInstance.tsx
ui/pages/TokenInstance.tsx
+182
-7
PageTitle.tsx
ui/shared/Page/PageTitle.tsx
+1
-1
TokenSnippet.tsx
ui/shared/TokenSnippet/TokenSnippet.tsx
+6
-2
TokenDetails.tsx
ui/token/TokenDetails.tsx
+8
-2
TokenTransfer.tsx
ui/token/TokenTransfer/TokenTransfer.tsx
+3
-3
TokenInstanceContent.tsx
ui/tokenInstance/TokenInstanceContent.tsx
+0
-177
TokenInstanceDetails.tsx
ui/tokenInstance/TokenInstanceDetails.tsx
+41
-18
TokenInstanceMetadata.tsx
ui/tokenInstance/TokenInstanceMetadata.tsx
+7
-1
TokenInstanceSkeleton.tsx
ui/tokenInstance/TokenInstanceSkeleton.tsx
+0
-40
TokenInstanceCreatorAddress.tsx
ui/tokenInstance/details/TokenInstanceCreatorAddress.tsx
+10
-9
TokenInstanceTransfersCount.tsx
ui/tokenInstance/details/TokenInstanceTransfersCount.tsx
+17
-12
No files found.
mocks/address/tokens.ts
View file @
70d95e92
...
@@ -7,54 +7,63 @@ export const erc20a: AddressTokenBalance = {
...
@@ -7,54 +7,63 @@ export const erc20a: AddressTokenBalance = {
token
:
tokens
.
tokenInfoERC20a
,
token
:
tokens
.
tokenInfoERC20a
,
token_id
:
null
,
token_id
:
null
,
value
:
'
1169320000000000000000000
'
,
value
:
'
1169320000000000000000000
'
,
token_instance
:
null
,
};
};
export
const
erc20b
:
AddressTokenBalance
=
{
export
const
erc20b
:
AddressTokenBalance
=
{
token
:
tokens
.
tokenInfoERC20b
,
token
:
tokens
.
tokenInfoERC20b
,
token_id
:
null
,
token_id
:
null
,
value
:
'
872500000000
'
,
value
:
'
872500000000
'
,
token_instance
:
null
,
};
};
export
const
erc20c
:
AddressTokenBalance
=
{
export
const
erc20c
:
AddressTokenBalance
=
{
token
:
tokens
.
tokenInfoERC20c
,
token
:
tokens
.
tokenInfoERC20c
,
token_id
:
null
,
token_id
:
null
,
value
:
'
9852000000000000000000
'
,
value
:
'
9852000000000000000000
'
,
token_instance
:
null
,
};
};
export
const
erc20d
:
AddressTokenBalance
=
{
export
const
erc20d
:
AddressTokenBalance
=
{
token
:
tokens
.
tokenInfoERC20d
,
token
:
tokens
.
tokenInfoERC20d
,
token_id
:
null
,
token_id
:
null
,
value
:
'
39000000000000000000
'
,
value
:
'
39000000000000000000
'
,
token_instance
:
null
,
};
};
export
const
erc20LongSymbol
:
AddressTokenBalance
=
{
export
const
erc20LongSymbol
:
AddressTokenBalance
=
{
token
:
tokens
.
tokenInfoERC20LongSymbol
,
token
:
tokens
.
tokenInfoERC20LongSymbol
,
token_id
:
null
,
token_id
:
null
,
value
:
'
39000000000000000000
'
,
value
:
'
39000000000000000000
'
,
token_instance
:
null
,
};
};
export
const
erc721a
:
AddressTokenBalance
=
{
export
const
erc721a
:
AddressTokenBalance
=
{
token
:
tokens
.
tokenInfoERC721a
,
token
:
tokens
.
tokenInfoERC721a
,
token_id
:
null
,
token_id
:
null
,
value
:
'
51
'
,
value
:
'
51
'
,
token_instance
:
null
,
};
};
export
const
erc721b
:
AddressTokenBalance
=
{
export
const
erc721b
:
AddressTokenBalance
=
{
token
:
tokens
.
tokenInfoERC721b
,
token
:
tokens
.
tokenInfoERC721b
,
token_id
:
null
,
token_id
:
null
,
value
:
'
1
'
,
value
:
'
1
'
,
token_instance
:
null
,
};
};
export
const
erc721c
:
AddressTokenBalance
=
{
export
const
erc721c
:
AddressTokenBalance
=
{
token
:
tokens
.
tokenInfoERC721c
,
token
:
tokens
.
tokenInfoERC721c
,
token_id
:
null
,
token_id
:
null
,
value
:
'
5
'
,
value
:
'
5
'
,
token_instance
:
null
,
};
};
export
const
erc721LongSymbol
:
AddressTokenBalance
=
{
export
const
erc721LongSymbol
:
AddressTokenBalance
=
{
token
:
tokens
.
tokenInfoERC721LongSymbol
,
token
:
tokens
.
tokenInfoERC721LongSymbol
,
token_id
:
null
,
token_id
:
null
,
value
:
'
5
'
,
value
:
'
5
'
,
token_instance
:
null
,
};
};
export
const
erc1155a
:
AddressTokenBalance
=
{
export
const
erc1155a
:
AddressTokenBalance
=
{
...
...
pages/token/[hash]/instance/[id].tsx
View file @
70d95e92
import
type
{
NextPage
}
from
'
next
'
;
import
type
{
NextPage
}
from
'
next
'
;
import
dynamic
from
'
next/dynamic
'
;
import
Head
from
'
next/head
'
;
import
Head
from
'
next/head
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
PageParams
}
from
'
lib/next/token/types
'
;
import
type
{
PageParams
}
from
'
lib/next/token/types
'
;
import
getNetworkTitle
from
'
lib/networks/getNetworkTitle
'
;
import
getNetworkTitle
from
'
lib/networks/getNetworkTitle
'
;
import
TokenInstance
from
'
ui/pages/TokenInstance
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
const
TokenInstance
=
dynamic
(()
=>
import
(
'
ui/pages/TokenInstance
'
),
{
ssr
:
false
});
const
TokenInstancePage
:
NextPage
<
PageParams
>
=
()
=>
{
const
TokenInstancePage
:
NextPage
<
PageParams
>
=
()
=>
{
const
title
=
getNetworkTitle
();
const
title
=
getNetworkTitle
();
...
@@ -15,7 +17,9 @@ const TokenInstancePage: NextPage<PageParams> = () => {
...
@@ -15,7 +17,9 @@ const TokenInstancePage: NextPage<PageParams> = () => {
<
Head
>
<
Head
>
<
title
>
{
title
}
</
title
>
<
title
>
{
title
}
</
title
>
</
Head
>
</
Head
>
<
TokenInstance
/>
<
Page
>
<
TokenInstance
/>
</
Page
>
</>
</>
);
);
};
};
...
...
stubs/address.ts
View file @
70d95e92
...
@@ -9,7 +9,7 @@ export const ADDRESS_INFO: Address = {
...
@@ -9,7 +9,7 @@ export const ADDRESS_INFO: Address = {
block_number_balance_updated_at
:
8774377
,
block_number_balance_updated_at
:
8774377
,
coin_balance
:
'
810941268802273085757
'
,
coin_balance
:
'
810941268802273085757
'
,
creation_tx_hash
:
null
,
creation_tx_hash
:
null
,
creator_address_hash
:
null
,
creator_address_hash
:
ADDRESS_HASH
,
exchange_rate
:
null
,
exchange_rate
:
null
,
has_custom_methods_read
:
false
,
has_custom_methods_read
:
false
,
has_custom_methods_write
:
false
,
has_custom_methods_write
:
false
,
...
...
stubs/token.ts
View file @
70d95e92
import
type
{
TokenCounters
,
TokenHolder
,
Token
Holders
,
Token
Info
,
TokenInstance
,
TokenType
}
from
'
types/api/token
'
;
import
type
{
TokenCounters
,
TokenHolder
,
TokenInfo
,
TokenInstance
,
TokenType
}
from
'
types/api/token
'
;
import
type
{
TokenTransfer
,
TokenTransferPagination
,
TokenTransferResponse
}
from
'
types/api/tokenTransfer
'
;
import
type
{
TokenTransfer
,
TokenTransferPagination
,
TokenTransferResponse
}
from
'
types/api/tokenTransfer
'
;
import
{
ADDRESS_PARAMS
,
ADDRESS_HASH
}
from
'
./addressParams
'
;
import
{
ADDRESS_PARAMS
,
ADDRESS_HASH
}
from
'
./addressParams
'
;
...
@@ -38,8 +38,6 @@ export const TOKEN_HOLDER: TokenHolder = {
...
@@ -38,8 +38,6 @@ export const TOKEN_HOLDER: TokenHolder = {
value
:
'
1021378038331138520
'
,
value
:
'
1021378038331138520
'
,
};
};
export
const
TOKEN_HOLDERS
:
TokenHolders
=
{
items
:
Array
(
50
).
fill
(
TOKEN_HOLDER
),
next_page_params
:
null
};
export
const
TOKEN_TRANSFER_ERC_20
:
TokenTransfer
=
{
export
const
TOKEN_TRANSFER_ERC_20
:
TokenTransfer
=
{
block_hash
:
BLOCK_HASH
,
block_hash
:
BLOCK_HASH
,
from
:
ADDRESS_PARAMS
,
from
:
ADDRESS_PARAMS
,
...
@@ -92,7 +90,7 @@ export const TOKEN_INSTANCE: TokenInstance = {
...
@@ -92,7 +90,7 @@ export const TOKEN_INSTANCE: TokenInstance = {
image_url
:
'
https://ipfs.vipsland.com/nft/collections/genesis/188882.gif
'
,
image_url
:
'
https://ipfs.vipsland.com/nft/collections/genesis/188882.gif
'
,
is_unique
:
true
,
is_unique
:
true
,
metadata
:
{
metadata
:
{
attributes
:
Array
(
3
).
fill
({
trait_type
:
'
skin
'
,
value
:
'
6
'
}),
attributes
:
Array
(
3
).
fill
({
trait_type
:
'
skin
tone
'
,
value
:
'
very light skin tone
'
}),
description
:
'
**GENESIS #188882**, **8a77ca1bcaa4036f** :: *844th* generation of *#57806 and #57809* :: **eGenetic Hash Code (eDNA)** = *2822355e953a462d*
'
,
description
:
'
**GENESIS #188882**, **8a77ca1bcaa4036f** :: *844th* generation of *#57806 and #57809* :: **eGenetic Hash Code (eDNA)** = *2822355e953a462d*
'
,
external_url
:
'
https://vipsland.com/nft/collections/genesis/188882
'
,
external_url
:
'
https://vipsland.com/nft/collections/genesis/188882
'
,
image
:
'
https://ipfs.vipsland.com/nft/collections/genesis/188882.gif
'
,
image
:
'
https://ipfs.vipsland.com/nft/collections/genesis/188882.gif
'
,
...
...
ui/pages/Token.tsx
View file @
70d95e92
...
@@ -136,7 +136,7 @@ const TokenPageContent = () => {
...
@@ -136,7 +136,7 @@ const TokenPageContent = () => {
scrollRef,
scrollRef,
options: {
options: {
enabled: Boolean(router.query.hash && router.query.tab === 'holders' && hasData),
enabled: Boolean(router.query.hash && router.query.tab === 'holders' && hasData),
placeholderData:
tokenStubs.TOKEN_HOLDERS
,
placeholderData:
generateListStub<'token_holders'>(tokenStubs.TOKEN_HOLDER, 50, { next_page_params: null })
,
},
},
});
});
...
...
ui/pages/TokenInstance.tsx
View file @
70d95e92
import
{
Box
,
Icon
,
Skeleton
}
from
'
@chakra-ui/react
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
import
type
{
RoutedTab
}
from
'
ui/shared/Tabs/types
'
;
import
TokenInstanceContent
from
'
ui/tokenInstance/TokenInstanceContent
'
;
import
nftIcon
from
'
icons/nft_shield.svg
'
;
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
{
useAppContext
}
from
'
lib/appContext
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
useQueryWithPages
from
'
lib/hooks/useQueryWithPages
'
;
import
{
TOKEN_INSTANCE
}
from
'
stubs/token
'
;
import
*
as
tokenStubs
from
'
stubs/token
'
;
import
{
generateListStub
}
from
'
stubs/utils
'
;
import
TextAd
from
'
ui/shared/ad/TextAd
'
;
import
AddressHeadingInfo
from
'
ui/shared/AddressHeadingInfo
'
;
import
Tag
from
'
ui/shared/chakra/Tag
'
;
import
LinkExternal
from
'
ui/shared/LinkExternal
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
import
Pagination
from
'
ui/shared/Pagination
'
;
import
type
{
Props
as
PaginationProps
}
from
'
ui/shared/Pagination
'
;
import
SkeletonTabs
from
'
ui/shared/skeletons/SkeletonTabs
'
;
import
RoutedTabs
from
'
ui/shared/Tabs/RoutedTabs
'
;
import
TokenHolders
from
'
ui/token/TokenHolders/TokenHolders
'
;
import
TokenTransfer
from
'
ui/token/TokenTransfer/TokenTransfer
'
;
import
TokenInstanceDetails
from
'
ui/tokenInstance/TokenInstanceDetails
'
;
import
TokenInstanceMetadata
from
'
ui/tokenInstance/TokenInstanceMetadata
'
;
export
type
TokenTabs
=
'
token_transfers
'
|
'
holders
'
export
type
TokenTabs
=
'
token_transfers
'
|
'
holders
'
const
TokenInstance
=
()
=>
{
const
TokenInstanceContent
=
()
=>
{
const
router
=
useRouter
();
const
isMobile
=
useIsMobile
();
const
appProps
=
useAppContext
();
const
hash
=
router
.
query
.
hash
?.
toString
();
const
id
=
router
.
query
.
id
?.
toString
();
const
tab
=
router
.
query
.
tab
?.
toString
();
const
scrollRef
=
React
.
useRef
<
HTMLDivElement
>
(
null
);
const
tokenInstanceQuery
=
useApiQuery
(
'
token_instance
'
,
{
pathParams
:
{
hash
,
id
},
queryOptions
:
{
enabled
:
Boolean
(
hash
&&
id
),
placeholderData
:
TOKEN_INSTANCE
,
},
});
const
transfersQuery
=
useQueryWithPages
({
resourceName
:
'
token_instance_transfers
'
,
pathParams
:
{
hash
,
id
},
scrollRef
,
options
:
{
enabled
:
Boolean
(
hash
&&
id
&&
(
!
tab
||
tab
===
'
token_transfers
'
)
&&
!
tokenInstanceQuery
.
isPlaceholderData
&&
tokenInstanceQuery
.
data
),
placeholderData
:
generateListStub
<
'
token_instance_transfers
'
>
(
tokenInstanceQuery
.
data
?.
token
.
type
===
'
ERC-1155
'
?
tokenStubs
.
TOKEN_TRANSFER_ERC_1155
:
tokenStubs
.
TOKEN_TRANSFER_ERC_721
,
10
,
{
next_page_params
:
null
},
),
},
});
const
shouldFetchHolders
=
!
tokenInstanceQuery
.
isPlaceholderData
&&
tokenInstanceQuery
.
data
&&
!
tokenInstanceQuery
.
data
.
is_unique
;
const
holdersQuery
=
useQueryWithPages
({
resourceName
:
'
token_instance_holders
'
,
pathParams
:
{
hash
,
id
},
scrollRef
,
options
:
{
enabled
:
Boolean
(
hash
&&
tab
===
'
holders
'
&&
shouldFetchHolders
),
placeholderData
:
generateListStub
<
'
token_instance_holders
'
>
(
tokenStubs
.
TOKEN_HOLDER
,
10
,
{
next_page_params
:
null
}),
},
});
const
backLink
=
React
.
useMemo
(()
=>
{
const
hasGoBackLink
=
appProps
.
referrer
&&
appProps
.
referrer
.
includes
(
`/token/
${
hash
}
`
)
&&
!
appProps
.
referrer
.
includes
(
'
instance
'
);
if
(
!
hasGoBackLink
)
{
return
;
}
return
{
label
:
'
Back to token page
'
,
url
:
appProps
.
referrer
,
};
},
[
appProps
.
referrer
,
hash
]);
const
tabs
:
Array
<
RoutedTab
>
=
[
{
id
:
'
token_transfers
'
,
title
:
'
Token transfers
'
,
component
:
<
TokenTransfer
transfersQuery=
{
transfersQuery
}
tokenId=
{
id
}
token=
{
tokenInstanceQuery
.
data
?.
token
}
/>,
},
shouldFetchHolders
?
{
id
:
'
holders
'
,
title
:
'
Holders
'
,
component
:
<
TokenHolders
holdersQuery=
{
holdersQuery
}
token=
{
tokenInstanceQuery
.
data
?.
token
}
/>
}
:
undefined
,
{
id
:
'
metadata
'
,
title
:
'
Metadata
'
,
component
:
(
<
TokenInstanceMetadata
data=
{
tokenInstanceQuery
.
data
?.
metadata
}
isPlaceholderData=
{
tokenInstanceQuery
.
isPlaceholderData
}
/>
)
},
].
filter
(
Boolean
);
if
(
tokenInstanceQuery
.
isError
)
{
throw
Error
(
'
Token instance fetch failed
'
,
{
cause
:
tokenInstanceQuery
.
error
});
}
const
nftShieldIcon
=
tokenInstanceQuery
.
isPlaceholderData
?
<
Skeleton
boxSize=
{
6
}
display=
"inline-block"
borderRadius=
"base"
mr=
{
2
}
my=
{
2
}
verticalAlign=
"text-bottom"
/>
:
<
Icon
as=
{
nftIcon
}
boxSize=
{
6
}
mr=
{
2
}
/>;
const
tokenTag
=
<
Tag
isLoading=
{
tokenInstanceQuery
.
isPlaceholderData
}
>
{
tokenInstanceQuery
.
data
?.
token
.
type
}
</
Tag
>;
const
address
=
{
hash
:
hash
||
''
,
is_contract
:
true
,
implementation_name
:
null
,
watchlist_names
:
[],
watchlist_address_id
:
null
,
};
const
appLink
=
(()
=>
{
if
(
!
tokenInstanceQuery
.
data
?.
external_app_url
)
{
return
null
;
}
try
{
const
url
=
new
URL
(
tokenInstanceQuery
.
data
.
external_app_url
);
return
(
<
Skeleton
isLoaded=
{
!
tokenInstanceQuery
.
isPlaceholderData
}
display=
"inline-block"
fontSize=
"sm"
mt=
{
6
}
>
<
span
>
View in app
</
span
>
<
LinkExternal
href=
{
tokenInstanceQuery
.
data
.
external_app_url
}
>
{
url
.
hostname
}
</
LinkExternal
>
</
Skeleton
>
);
}
catch
(
error
)
{
return
(
<
Box
fontSize=
"sm"
mt=
{
6
}
>
<
LinkExternal
href=
{
tokenInstanceQuery
.
data
.
external_app_url
}
>
View in app
</
LinkExternal
>
</
Box
>
);
}
})();
let
pagination
:
PaginationProps
|
undefined
;
let
isPaginationVisible
;
if
(
tab
===
'
token_transfers
'
)
{
pagination
=
transfersQuery
.
pagination
;
isPaginationVisible
=
transfersQuery
.
isPaginationVisible
;
}
else
if
(
tab
===
'
holders
'
)
{
pagination
=
holdersQuery
.
pagination
;
isPaginationVisible
=
holdersQuery
.
isPaginationVisible
;
}
return
(
return
(
<
Page
>
<>
<
TokenInstanceContent
/>
<
TextAd
mb=
{
6
}
/>
</
Page
>
<
PageTitle
title=
{
`${ tokenInstanceQuery.data?.token.name || 'Unnamed token' } #${ tokenInstanceQuery.data?.id }`
}
backLink=
{
backLink
}
beforeTitle=
{
nftShieldIcon
}
contentAfter=
{
tokenTag
}
isLoading=
{
tokenInstanceQuery
.
isPlaceholderData
}
/>
<
AddressHeadingInfo
address=
{
address
}
token=
{
tokenInstanceQuery
.
data
?.
token
}
isLoading=
{
tokenInstanceQuery
.
isPlaceholderData
}
/>
{
appLink
}
<
TokenInstanceDetails
data=
{
tokenInstanceQuery
?.
data
}
isLoading=
{
tokenInstanceQuery
.
isPlaceholderData
}
scrollRef=
{
scrollRef
}
/>
{
/* should stay before tabs to scroll up with pagination */
}
<
Box
ref=
{
scrollRef
}
></
Box
>
{
tokenInstanceQuery
.
isPlaceholderData
?
<
SkeletonTabs
tabs=
{
tabs
}
/>
:
(
<
RoutedTabs
tabs=
{
tabs
}
tabListProps=
{
isMobile
?
{
mt
:
8
}
:
{
mt
:
3
,
py
:
5
,
marginBottom
:
0
}
}
rightSlot=
{
!
isMobile
&&
isPaginationVisible
&&
pagination
?
<
Pagination
{
...
pagination
}
/>
:
null
}
stickyEnabled=
{
!
isMobile
}
/>
)
}
{
!
tokenInstanceQuery
.
isLoading
&&
!
tokenInstanceQuery
.
isError
&&
<
Box
h=
{
{
base
:
0
,
lg
:
'
40vh
'
}
}
/>
}
</>
);
);
};
};
export
default
TokenInstance
;
export
default
React
.
memo
(
TokenInstanceContent
)
;
ui/shared/Page/PageTitle.tsx
View file @
70d95e92
...
@@ -24,7 +24,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
...
@@ -24,7 +24,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
}
}
if
(
props
.
isLoading
)
{
if
(
props
.
isLoading
)
{
return
<
Skeleton
boxSize=
{
6
}
display=
"inline-block"
borderRadius=
"base"
mr=
{
3
}
isLoaded=
{
!
props
.
isLoading
}
/>;
return
<
Skeleton
boxSize=
{
6
}
display=
"inline-block"
borderRadius=
"base"
mr=
{
3
}
my=
{
2
}
verticalAlign=
"text-bottom"
isLoaded=
{
!
props
.
isLoading
}
/>;
}
}
const
icon
=
<
Icon
as=
{
eastArrowIcon
}
boxSize=
{
6
}
transform=
"rotate(180deg)"
margin=
"auto"
/>;
const
icon
=
<
Icon
as=
{
eastArrowIcon
}
boxSize=
{
6
}
transform=
"rotate(180deg)"
margin=
"auto"
/>;
...
...
ui/shared/TokenSnippet/TokenSnippet.tsx
View file @
70d95e92
import
{
Flex
,
Text
,
chakra
}
from
'
@chakra-ui/react
'
;
import
{
Flex
,
chakra
,
Skeleton
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
TokenInfo
}
from
'
types/api/token
'
;
import
type
{
TokenInfo
}
from
'
types/api/token
'
;
...
@@ -21,7 +21,11 @@ const TokenSnippet = ({ data, className, logoSize = 6, isDisabled, hideSymbol, i
...
@@ -21,7 +21,11 @@ const TokenSnippet = ({ data, className, logoSize = 6, isDisabled, hideSymbol, i
<
Flex
className=
{
className
}
alignItems=
"center"
columnGap=
{
2
}
w=
"100%"
>
<
Flex
className=
{
className
}
alignItems=
"center"
columnGap=
{
2
}
w=
"100%"
>
<
TokenLogo
boxSize=
{
logoSize
}
data=
{
data
}
isLoading=
{
isLoading
}
/>
<
TokenLogo
boxSize=
{
logoSize
}
data=
{
data
}
isLoading=
{
isLoading
}
/>
<
AddressLink
hash=
{
data
?.
address
||
''
}
alias=
{
data
?.
name
||
'
Unnamed token
'
}
type=
"token"
isDisabled=
{
isDisabled
}
isLoading=
{
isLoading
}
/>
<
AddressLink
hash=
{
data
?.
address
||
''
}
alias=
{
data
?.
name
||
'
Unnamed token
'
}
type=
"token"
isDisabled=
{
isDisabled
}
isLoading=
{
isLoading
}
/>
{
data
?.
symbol
&&
!
hideSymbol
&&
<
Text
variant=
"secondary"
>
(
{
trimTokenSymbol
(
data
.
symbol
)
}
)
</
Text
>
}
{
data
?.
symbol
&&
!
hideSymbol
&&
(
<
Skeleton
isLoaded=
{
!
isLoading
}
color=
"text_secondary"
>
<
span
>
(
{
trimTokenSymbol
(
data
.
symbol
)
}
)
</
span
>
</
Skeleton
>
)
}
</
Flex
>
</
Flex
>
);
);
};
};
...
...
ui/token/TokenDetails.tsx
View file @
70d95e92
...
@@ -87,8 +87,11 @@ const TokenDetails = ({ tokenQuery }: Props) => {
...
@@ -87,8 +87,11 @@ const TokenDetails = ({ tokenQuery }: Props) => {
title=
"Price"
title=
"Price"
hint=
"Price per token on the exchanges"
hint=
"Price per token on the exchanges"
alignSelf=
"center"
alignSelf=
"center"
isLoading=
{
tokenQuery
.
isPlaceholderData
}
>
>
{
`$${ exchangeRate }`
}
<
Skeleton
isLoaded=
{
!
tokenQuery
.
isPlaceholderData
}
display=
"inline-block"
>
<
span
>
{
`$${ exchangeRate }`
}
</
span
>
</
Skeleton
>
</
DetailsInfoItem
>
</
DetailsInfoItem
>
)
}
)
}
{
marketcap
&&
(
{
marketcap
&&
(
...
@@ -96,8 +99,11 @@ const TokenDetails = ({ tokenQuery }: Props) => {
...
@@ -96,8 +99,11 @@ const TokenDetails = ({ tokenQuery }: Props) => {
title=
"Fully diluted market cap"
title=
"Fully diluted market cap"
hint=
"Total supply * Price"
hint=
"Total supply * Price"
alignSelf=
"center"
alignSelf=
"center"
isLoading=
{
tokenQuery
.
isPlaceholderData
}
>
>
{
`$${ marketcap }`
}
<
Skeleton
isLoaded=
{
!
tokenQuery
.
isPlaceholderData
}
display=
"inline-block"
>
<
span
>
{
`$${ marketcap }`
}
</
span
>
</
Skeleton
>
</
DetailsInfoItem
>
</
DetailsInfoItem
>
)
}
)
}
<
DetailsInfoItem
<
DetailsInfoItem
...
...
ui/token/TokenTransfer/TokenTransfer.tsx
View file @
70d95e92
...
@@ -31,7 +31,7 @@ type Props = {
...
@@ -31,7 +31,7 @@ type Props = {
const
TokenTransfer
=
({
transfersQuery
,
tokenId
,
token
}:
Props
)
=>
{
const
TokenTransfer
=
({
transfersQuery
,
tokenId
,
token
}:
Props
)
=>
{
const
isMobile
=
useIsMobile
();
const
isMobile
=
useIsMobile
();
const
router
=
useRouter
();
const
router
=
useRouter
();
const
{
isError
,
is
Loading
,
is
PlaceholderData
,
data
,
pagination
,
isPaginationVisible
}
=
transfersQuery
;
const
{
isError
,
isPlaceholderData
,
data
,
pagination
,
isPaginationVisible
}
=
transfersQuery
;
const
[
newItemsCount
,
setNewItemsCount
]
=
useGradualIncrement
(
0
);
const
[
newItemsCount
,
setNewItemsCount
]
=
useGradualIncrement
(
0
);
const
[
socketAlert
,
setSocketAlert
]
=
React
.
useState
(
''
);
const
[
socketAlert
,
setSocketAlert
]
=
React
.
useState
(
''
);
...
@@ -52,7 +52,7 @@ const TokenTransfer = ({ transfersQuery, tokenId, token }: Props) => {
...
@@ -52,7 +52,7 @@ const TokenTransfer = ({ transfersQuery, tokenId, token }: Props) => {
topic
:
`tokens:
${
router
.
query
.
hash
?.
toString
().
toLowerCase
()
}
`,
topic
:
`tokens:
${
router
.
query
.
hash
?.
toString
().
toLowerCase
()
}
`,
onSocketClose: handleSocketClose,
onSocketClose: handleSocketClose,
onSocketError: handleSocketError,
onSocketError: handleSocketError,
isDisabled: is
Loading
|| isError || pagination.page !== 1,
isDisabled: is
PlaceholderData
|| isError || pagination.page !== 1,
});
});
useSocketMessage({
useSocketMessage({
channel,
channel,
...
@@ -99,7 +99,7 @@ const TokenTransfer = ({ transfersQuery, tokenId, token }: Props) => {
...
@@ -99,7 +99,7 @@ const TokenTransfer = ({ transfersQuery, tokenId, token }: Props) => {
return (
return (
<DataListDisplay
<DataListDisplay
isError={ isError }
isError={ isError }
isLoading={
!isPlaceholderData && isLoading
}
isLoading={
false
}
items={ data?.items }
items={ data?.items }
skeletonProps={{
skeletonProps={{
isLongSkeleton: true,
isLongSkeleton: true,
...
...
ui/tokenInstance/TokenInstanceContent.tsx
deleted
100644 → 0
View file @
dd1daf82
import
{
Box
,
Tag
,
Icon
}
from
'
@chakra-ui/react
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
import
type
{
RoutedTab
}
from
'
ui/shared/Tabs/types
'
;
import
nftIcon
from
'
icons/nft_shield.svg
'
;
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
{
useAppContext
}
from
'
lib/appContext
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
useQueryWithPages
from
'
lib/hooks/useQueryWithPages
'
;
import
TextAd
from
'
ui/shared/ad/TextAd
'
;
import
AddressHeadingInfo
from
'
ui/shared/AddressHeadingInfo
'
;
import
LinkExternal
from
'
ui/shared/LinkExternal
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
import
Pagination
from
'
ui/shared/Pagination
'
;
import
type
{
Props
as
PaginationProps
}
from
'
ui/shared/Pagination
'
;
import
SkeletonTabs
from
'
ui/shared/skeletons/SkeletonTabs
'
;
import
RoutedTabs
from
'
ui/shared/Tabs/RoutedTabs
'
;
import
TokenHolders
from
'
ui/token/TokenHolders/TokenHolders
'
;
import
TokenTransfer
from
'
ui/token/TokenTransfer/TokenTransfer
'
;
import
TokenInstanceDetails
from
'
./TokenInstanceDetails
'
;
import
TokenInstanceMetadata
from
'
./TokenInstanceMetadata
'
;
import
TokenInstanceSkeleton
from
'
./TokenInstanceSkeleton
'
;
export
type
TokenTabs
=
'
token_transfers
'
|
'
holders
'
const
TokenInstanceContent
=
()
=>
{
const
router
=
useRouter
();
const
isMobile
=
useIsMobile
();
const
appProps
=
useAppContext
();
const
hash
=
router
.
query
.
hash
?.
toString
();
const
id
=
router
.
query
.
id
?.
toString
();
const
tab
=
router
.
query
.
tab
?.
toString
();
const
scrollRef
=
React
.
useRef
<
HTMLDivElement
>
(
null
);
const
tokenInstanceQuery
=
useApiQuery
(
'
token_instance
'
,
{
pathParams
:
{
hash
,
id
},
queryOptions
:
{
enabled
:
Boolean
(
hash
&&
id
)
},
});
const
transfersQuery
=
useQueryWithPages
({
resourceName
:
'
token_instance_transfers
'
,
pathParams
:
{
hash
,
id
},
scrollRef
,
options
:
{
enabled
:
Boolean
(
hash
&&
(
!
tab
||
tab
===
'
token_transfers
'
)
&&
tokenInstanceQuery
.
data
),
},
});
const
shouldFetchHolders
=
tokenInstanceQuery
.
data
&&
!
tokenInstanceQuery
.
data
.
is_unique
;
const
holdersQuery
=
useQueryWithPages
({
resourceName
:
'
token_instance_holders
'
,
pathParams
:
{
hash
,
id
},
scrollRef
,
options
:
{
enabled
:
Boolean
(
hash
&&
(
!
tab
||
tab
===
'
holders
'
)
&&
shouldFetchHolders
),
},
});
const
backLink
=
React
.
useMemo
(()
=>
{
const
hasGoBackLink
=
appProps
.
referrer
&&
appProps
.
referrer
.
includes
(
`/token/
${
hash
}
`
)
&&
!
appProps
.
referrer
.
includes
(
'
instance
'
);
if
(
!
hasGoBackLink
)
{
return
;
}
return
{
label
:
'
Back to token page
'
,
url
:
appProps
.
referrer
,
};
},
[
appProps
.
referrer
,
hash
]);
const
tabs
:
Array
<
RoutedTab
>
=
[
{
id
:
'
token_transfers
'
,
title
:
'
Token transfers
'
,
component
:
<
TokenTransfer
transfersQuery=
{
transfersQuery
}
tokenId=
{
id
}
token=
{
tokenInstanceQuery
.
data
?.
token
}
/>,
},
shouldFetchHolders
?
{
id
:
'
holders
'
,
title
:
'
Holders
'
,
component
:
<
TokenHolders
holdersQuery=
{
holdersQuery
}
token=
{
tokenInstanceQuery
.
data
?.
token
}
/>
}
:
undefined
,
{
id
:
'
metadata
'
,
title
:
'
Metadata
'
,
component
:
<
TokenInstanceMetadata
data=
{
tokenInstanceQuery
.
data
?.
metadata
}
/>
},
].
filter
(
Boolean
);
if
(
tokenInstanceQuery
.
isError
)
{
throw
Error
(
'
Token instance fetch failed
'
,
{
cause
:
tokenInstanceQuery
.
error
});
}
if
(
tokenInstanceQuery
.
isLoading
)
{
return
<
TokenInstanceSkeleton
/>;
}
const
nftShieldIcon
=
<
Icon
as=
{
nftIcon
}
boxSize=
{
6
}
mr=
{
2
}
/>;
const
tokenTag
=
<
Tag
>
{
tokenInstanceQuery
.
data
.
token
.
type
}
</
Tag
>;
const
address
=
{
hash
:
hash
||
''
,
is_contract
:
true
,
implementation_name
:
null
,
watchlist_names
:
[],
watchlist_address_id
:
null
,
};
const
appLink
=
(()
=>
{
if
(
!
tokenInstanceQuery
.
data
.
external_app_url
)
{
return
null
;
}
try
{
const
url
=
new
URL
(
tokenInstanceQuery
.
data
.
external_app_url
);
return
(
<
Box
fontSize=
"sm"
mt=
{
6
}
>
<
span
>
View in app
</
span
>
<
LinkExternal
href=
{
tokenInstanceQuery
.
data
.
external_app_url
}
>
{
url
.
hostname
}
</
LinkExternal
>
</
Box
>
);
}
catch
(
error
)
{
return
(
<
Box
fontSize=
"sm"
mt=
{
6
}
>
<
LinkExternal
href=
{
tokenInstanceQuery
.
data
.
external_app_url
}
>
View in app
</
LinkExternal
>
</
Box
>
);
}
})();
let
pagination
:
PaginationProps
|
undefined
;
let
isPaginationVisible
;
if
(
tab
===
'
token_transfers
'
)
{
pagination
=
transfersQuery
.
pagination
;
isPaginationVisible
=
transfersQuery
.
isPaginationVisible
;
}
else
if
(
tab
===
'
holders
'
)
{
pagination
=
holdersQuery
.
pagination
;
isPaginationVisible
=
holdersQuery
.
isPaginationVisible
;
}
return
(
<>
<
TextAd
mb=
{
6
}
/>
<
PageTitle
title=
{
`${ tokenInstanceQuery.data.token.name || 'Unnamed token' } #${ tokenInstanceQuery.data.id }`
}
backLink=
{
backLink
}
beforeTitle=
{
nftShieldIcon
}
contentAfter=
{
tokenTag
}
/>
<
AddressHeadingInfo
address=
{
address
}
token=
{
tokenInstanceQuery
.
data
.
token
}
/>
{
appLink
}
<
TokenInstanceDetails
data=
{
tokenInstanceQuery
.
data
}
scrollRef=
{
scrollRef
}
/>
{
/* should stay before tabs to scroll up with pagination */
}
<
Box
ref=
{
scrollRef
}
></
Box
>
{
tokenInstanceQuery
.
isLoading
?
<
SkeletonTabs
/>
:
(
<
RoutedTabs
tabs=
{
tabs
}
tabListProps=
{
isMobile
?
{
mt
:
8
}
:
{
mt
:
3
,
py
:
5
,
marginBottom
:
0
}
}
rightSlot=
{
!
isMobile
&&
isPaginationVisible
&&
pagination
?
<
Pagination
{
...
pagination
}
/>
:
null
}
stickyEnabled=
{
!
isMobile
}
/>
)
}
{
!
tokenInstanceQuery
.
isLoading
&&
!
tokenInstanceQuery
.
isError
&&
<
Box
h=
{
{
base
:
0
,
lg
:
'
40vh
'
}
}
/>
}
</>
);
};
export
default
React
.
memo
(
TokenInstanceContent
);
ui/tokenInstance/TokenInstanceDetails.tsx
View file @
70d95e92
import
{
Box
,
Flex
,
Grid
,
GridItem
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
{
Flex
,
Grid
,
GridItem
,
Skeleton
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
TokenInstance
}
from
'
types/api/token
'
;
import
type
{
TokenInstance
}
from
'
types/api/token
'
;
...
@@ -18,11 +18,12 @@ import TokenInstanceCreatorAddress from './details/TokenInstanceCreatorAddress';
...
@@ -18,11 +18,12 @@ import TokenInstanceCreatorAddress from './details/TokenInstanceCreatorAddress';
import
TokenInstanceTransfersCount
from
'
./details/TokenInstanceTransfersCount
'
;
import
TokenInstanceTransfersCount
from
'
./details/TokenInstanceTransfersCount
'
;
interface
Props
{
interface
Props
{
data
:
TokenInstance
;
data
?:
TokenInstance
;
isLoading
?:
boolean
;
scrollRef
?:
React
.
RefObject
<
HTMLDivElement
>
;
scrollRef
?:
React
.
RefObject
<
HTMLDivElement
>
;
}
}
const
TokenInstanceDetails
=
({
data
,
scrollRef
}:
Props
)
=>
{
const
TokenInstanceDetails
=
({
data
,
scrollRef
,
isLoading
}:
Props
)
=>
{
const
handleCounterItemClick
=
React
.
useCallback
(()
=>
{
const
handleCounterItemClick
=
React
.
useCallback
(()
=>
{
window
.
setTimeout
(()
=>
{
window
.
setTimeout
(()
=>
{
// cannot do scroll instantly, have to wait a little
// cannot do scroll instantly, have to wait a little
...
@@ -30,7 +31,7 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
...
@@ -30,7 +31,7 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
},
500
);
},
500
);
},
[
scrollRef
]);
},
[
scrollRef
]);
const
metadata
=
parseMetadata
(
data
.
metadata
);
const
metadata
=
parseMetadata
(
data
?
.
metadata
);
const
hasMetadata
=
metadata
&&
Boolean
((
metadata
.
name
||
metadata
.
description
||
metadata
.
attributes
));
const
hasMetadata
=
metadata
&&
Boolean
((
metadata
.
name
||
metadata
.
description
||
metadata
.
attributes
));
const
attributeBgColor
=
useColorModeValue
(
'
blackAlpha.50
'
,
'
whiteAlpha.50
'
);
const
attributeBgColor
=
useColorModeValue
(
'
blackAlpha.50
'
,
'
whiteAlpha.50
'
);
...
@@ -44,6 +45,10 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
...
@@ -44,6 +45,10 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
/>
/>
);
);
if
(
!
data
)
{
return
null
;
}
return
(
return
(
<>
<>
<
Flex
alignItems=
"flex-start"
mt=
{
8
}
flexDir=
{
{
base
:
'
column-reverse
'
,
lg
:
'
row
'
}
}
columnGap=
{
6
}
rowGap=
{
6
}
>
<
Flex
alignItems=
"flex-start"
mt=
{
8
}
flexDir=
{
{
base
:
'
column-reverse
'
,
lg
:
'
row
'
}
}
columnGap=
{
6
}
rowGap=
{
6
}
>
...
@@ -57,34 +62,37 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
...
@@ -57,34 +62,37 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
<
DetailsInfoItem
<
DetailsInfoItem
title=
"Token"
title=
"Token"
hint=
"Token name"
hint=
"Token name"
isLoading=
{
isLoading
}
>
>
<
TokenSnippet
data=
{
data
.
token
}
/>
<
TokenSnippet
data=
{
data
.
token
}
isLoading=
{
isLoading
}
/>
</
DetailsInfoItem
>
</
DetailsInfoItem
>
{
data
.
is_unique
&&
data
.
owner
&&
(
{
data
.
is_unique
&&
data
.
owner
&&
(
<
DetailsInfoItem
<
DetailsInfoItem
title=
"Owner"
title=
"Owner"
hint=
"Current owner of this token instance"
hint=
"Current owner of this token instance"
isLoading=
{
isLoading
}
>
>
<
Address
>
<
Address
>
<
AddressIcon
address=
{
data
.
owner
}
/>
<
AddressIcon
address=
{
data
.
owner
}
isLoading=
{
isLoading
}
/>
<
AddressLink
type=
"address"
hash=
{
data
.
owner
.
hash
}
ml=
{
2
}
/>
<
AddressLink
type=
"address"
hash=
{
data
.
owner
.
hash
}
ml=
{
2
}
isLoading=
{
isLoading
}
/>
<
CopyToClipboard
text=
{
data
.
owner
.
hash
}
/>
<
CopyToClipboard
text=
{
data
.
owner
.
hash
}
isLoading=
{
isLoading
}
/>
</
Address
>
</
Address
>
</
DetailsInfoItem
>
</
DetailsInfoItem
>
)
}
)
}
<
TokenInstanceCreatorAddress
hash=
{
data
.
token
.
address
}
/>
<
TokenInstanceCreatorAddress
hash=
{
isLoading
?
''
:
data
.
token
.
address
}
/>
<
DetailsInfoItem
<
DetailsInfoItem
title=
"Token ID"
title=
"Token ID"
hint=
"This token instance unique token ID"
hint=
"This token instance unique token ID"
isLoading=
{
isLoading
}
>
>
<
Flex
alignItems=
"center"
overflow=
"hidden"
>
<
Flex
alignItems=
"center"
overflow=
"hidden"
>
<
Box
overflow=
"hidden"
display=
"inline-block"
w=
"100%"
>
<
Skeleton
isLoaded=
{
!
isLoading
}
overflow=
"hidden"
display=
"inline-block"
w=
"100%"
>
<
HashStringShortenDynamic
hash=
{
data
.
id
}
/>
<
HashStringShortenDynamic
hash=
{
data
.
id
}
/>
</
Box
>
</
Skeleton
>
<
CopyToClipboard
text=
{
data
.
id
}
ml=
{
1
}
/>
<
CopyToClipboard
text=
{
data
.
id
}
isLoading=
{
isLoading
}
/>
</
Flex
>
</
Flex
>
</
DetailsInfoItem
>
</
DetailsInfoItem
>
<
TokenInstanceTransfersCount
hash=
{
data
.
token
.
address
}
id=
{
data
.
id
}
onClick=
{
handleCounterItemClick
}
/>
<
TokenInstanceTransfersCount
hash=
{
isLoading
?
''
:
data
.
token
.
address
}
id=
{
isLoading
?
''
:
data
.
id
}
onClick=
{
handleCounterItemClick
}
/>
</
Grid
>
</
Grid
>
<
NftMedia
<
NftMedia
imageUrl=
{
data
.
image_url
}
imageUrl=
{
data
.
image_url
}
...
@@ -92,6 +100,7 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
...
@@ -92,6 +100,7 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
w=
"250px"
w=
"250px"
flexShrink=
{
0
}
flexShrink=
{
0
}
alignSelf=
{
{
base
:
'
center
'
,
lg
:
'
flex-start
'
}
}
alignSelf=
{
{
base
:
'
center
'
,
lg
:
'
flex-start
'
}
}
isLoading=
{
isLoading
}
/>
/>
</
Flex
>
</
Flex
>
<
Grid
<
Grid
...
@@ -110,8 +119,11 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
...
@@ -110,8 +119,11 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
hint=
"NFT name"
hint=
"NFT name"
whiteSpace=
"normal"
whiteSpace=
"normal"
wordBreak=
"break-word"
wordBreak=
"break-word"
isLoading=
{
isLoading
}
>
>
{
metadata
.
name
}
<
Skeleton
isLoaded=
{
!
isLoading
}
>
{
metadata
.
name
}
</
Skeleton
>
</
DetailsInfoItem
>
</
DetailsInfoItem
>
)
}
)
}
{
metadata
?.
description
&&
(
{
metadata
?.
description
&&
(
...
@@ -120,8 +132,11 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
...
@@ -120,8 +132,11 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
hint=
"NFT description"
hint=
"NFT description"
whiteSpace=
"normal"
whiteSpace=
"normal"
wordBreak=
"break-word"
wordBreak=
"break-word"
isLoading=
{
isLoading
}
>
>
{
metadata
.
description
}
<
Skeleton
isLoaded=
{
!
isLoading
}
>
{
metadata
.
description
}
</
Skeleton
>
</
DetailsInfoItem
>
</
DetailsInfoItem
>
)
}
)
}
{
metadata
?.
attributes
&&
(
{
metadata
?.
attributes
&&
(
...
@@ -129,6 +144,7 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
...
@@ -129,6 +144,7 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
title=
"Attributes"
title=
"Attributes"
hint=
"NFT attributes"
hint=
"NFT attributes"
whiteSpace=
"normal"
whiteSpace=
"normal"
isLoading=
{
isLoading
}
>
>
<
Grid
gap=
{
2
}
templateColumns=
"repeat(auto-fill,minmax(160px, 1fr))"
w=
"100%"
>
<
Grid
gap=
{
2
}
templateColumns=
"repeat(auto-fill,minmax(160px, 1fr))"
w=
"100%"
>
{
metadata
.
attributes
.
map
((
attribute
,
index
)
=>
(
{
metadata
.
attributes
.
map
((
attribute
,
index
)
=>
(
...
@@ -138,9 +154,16 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
...
@@ -138,9 +154,16 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
borderRadius=
"md"
borderRadius=
"md"
px=
{
4
}
px=
{
4
}
py=
{
2
}
py=
{
2
}
display=
"flex"
flexDir=
"column"
alignItems=
"flex-start"
>
>
<
Box
fontSize=
"xs"
color=
"text_secondary"
fontWeight=
{
500
}
>
{
attribute
.
trait_type
}
</
Box
>
<
Skeleton
isLoaded=
{
!
isLoading
}
fontSize=
"xs"
lineHeight=
{
4
}
color=
"text_secondary"
fontWeight=
{
500
}
mb=
{
1
}
>
<
Box
fontSize=
"sm"
>
{
attribute
.
value
}
</
Box
>
<
span
>
{
attribute
.
trait_type
}
</
span
>
</
Skeleton
>
<
Skeleton
isLoaded=
{
!
isLoading
}
fontSize=
"sm"
>
<
span
>
{
attribute
.
value
}
</
span
>
</
Skeleton
>
</
GridItem
>
</
GridItem
>
))
}
))
}
</
Grid
>
</
Grid
>
...
@@ -149,7 +172,7 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
...
@@ -149,7 +172,7 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
</>
</>
)
}
)
}
{
divider
}
{
divider
}
<
DetailsSponsoredItem
/>
<
DetailsSponsoredItem
isLoading=
{
isLoading
}
/>
</
Grid
>
</
Grid
>
</>
</>
);
);
...
...
ui/tokenInstance/TokenInstanceMetadata.tsx
View file @
70d95e92
...
@@ -3,6 +3,7 @@ import React from 'react';
...
@@ -3,6 +3,7 @@ import React from 'react';
import
type
{
TokenInstance
}
from
'
types/api/token
'
;
import
type
{
TokenInstance
}
from
'
types/api/token
'
;
import
ContentLoader
from
'
ui/shared/ContentLoader
'
;
import
CopyToClipboard
from
'
ui/shared/CopyToClipboard
'
;
import
CopyToClipboard
from
'
ui/shared/CopyToClipboard
'
;
import
RawDataSnippet
from
'
ui/shared/RawDataSnippet
'
;
import
RawDataSnippet
from
'
ui/shared/RawDataSnippet
'
;
...
@@ -12,15 +13,20 @@ type Format = 'JSON' | 'Table'
...
@@ -12,15 +13,20 @@ type Format = 'JSON' | 'Table'
interface
Props
{
interface
Props
{
data
:
TokenInstance
[
'
metadata
'
]
|
undefined
;
data
:
TokenInstance
[
'
metadata
'
]
|
undefined
;
isPlaceholderData
?:
boolean
;
}
}
const
TokenInstanceMetadata
=
({
data
}:
Props
)
=>
{
const
TokenInstanceMetadata
=
({
data
,
isPlaceholderData
}:
Props
)
=>
{
const
[
format
,
setFormat
]
=
React
.
useState
<
Format
>
(
'
Table
'
);
const
[
format
,
setFormat
]
=
React
.
useState
<
Format
>
(
'
Table
'
);
const
handleSelectChange
=
React
.
useCallback
((
event
:
React
.
ChangeEvent
<
HTMLSelectElement
>
)
=>
{
const
handleSelectChange
=
React
.
useCallback
((
event
:
React
.
ChangeEvent
<
HTMLSelectElement
>
)
=>
{
setFormat
(
event
.
target
.
value
as
Format
);
setFormat
(
event
.
target
.
value
as
Format
);
},
[]);
},
[]);
if
(
isPlaceholderData
)
{
return
<
ContentLoader
/>;
}
if
(
!
data
)
{
if
(
!
data
)
{
return
<
Box
>
There is no metadata for this NFT
</
Box
>;
return
<
Box
>
There is no metadata for this NFT
</
Box
>;
}
}
...
...
ui/tokenInstance/TokenInstanceSkeleton.tsx
deleted
100644 → 0
View file @
dd1daf82
import
{
Box
,
Flex
,
Grid
,
Skeleton
,
SkeletonCircle
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
DetailsSkeletonRow
from
'
ui/shared/skeletons/DetailsSkeletonRow
'
;
import
SkeletonTabs
from
'
ui/shared/skeletons/SkeletonTabs
'
;
const
TokenInstanceSkeleton
=
()
=>
{
return
(
<
Box
>
<
Skeleton
h=
{
{
base
:
12
,
lg
:
6
}
}
mb=
{
6
}
w=
"100%"
maxW=
"680px"
/>
<
Skeleton
h=
{
10
}
maxW=
"400px"
w=
"100%"
mb=
{
6
}
/>
<
Flex
align=
"center"
>
<
SkeletonCircle
boxSize=
{
6
}
flexShrink=
{
0
}
/>
<
Skeleton
h=
{
6
}
w=
{
{
base
:
'
100px
'
,
lg
:
'
420px
'
}
}
ml=
{
2
}
borderRadius=
"full"
/>
<
Skeleton
h=
{
8
}
w=
"36px"
ml=
{
3
}
flexShrink=
{
0
}
/>
<
Skeleton
h=
{
8
}
w=
"36px"
ml=
{
3
}
flexShrink=
{
0
}
/>
</
Flex
>
<
Flex
columnGap=
{
6
}
rowGap=
{
6
}
alignItems=
"flex-start"
flexDir=
{
{
base
:
'
column-reverse
'
,
lg
:
'
row
'
}
}
mt=
{
8
}
>
<
Grid
columnGap=
{
8
}
rowGap=
{
{
base
:
5
,
lg
:
7
}
}
templateColumns=
{
{
base
:
'
1fr
'
,
lg
:
'
200px 1fr
'
}
}
flexGrow=
{
1
}
w=
"100%"
>
<
DetailsSkeletonRow
w=
"30%"
/>
<
DetailsSkeletonRow
w=
"100%"
maxW=
"450px"
/>
<
DetailsSkeletonRow
w=
"100%"
maxW=
"450px"
/>
<
DetailsSkeletonRow
w=
"10%"
/>
<
DetailsSkeletonRow
w=
"10%"
/>
<
DetailsSkeletonRow
w=
"10%"
/>
</
Grid
>
<
Skeleton
h=
"250px"
w=
"250px"
flexShrink=
{
0
}
alignSelf=
"center"
/>
</
Flex
>
<
SkeletonTabs
/>
</
Box
>
);
};
export
default
TokenInstanceSkeleton
;
ui/tokenInstance/details/TokenInstanceCreatorAddress.tsx
View file @
70d95e92
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
{
ADDRESS_INFO
}
from
'
stubs/address
'
;
import
Address
from
'
ui/shared/address/Address
'
;
import
Address
from
'
ui/shared/address/Address
'
;
import
AddressIcon
from
'
ui/shared/address/AddressIcon
'
;
import
AddressIcon
from
'
ui/shared/address/AddressIcon
'
;
import
AddressLink
from
'
ui/shared/address/AddressLink
'
;
import
AddressLink
from
'
ui/shared/address/AddressLink
'
;
import
CopyToClipboard
from
'
ui/shared/CopyToClipboard
'
;
import
CopyToClipboard
from
'
ui/shared/CopyToClipboard
'
;
import
DetailsInfoItem
from
'
ui/shared/DetailsInfoItem
'
;
import
DetailsInfoItem
from
'
ui/shared/DetailsInfoItem
'
;
import
DetailsSkeletonRow
from
'
ui/shared/skeletons/DetailsSkeletonRow
'
;
interface
Props
{
interface
Props
{
hash
:
string
;
hash
:
string
;
...
@@ -15,17 +15,17 @@ interface Props {
...
@@ -15,17 +15,17 @@ interface Props {
const
TokenInstanceCreatorAddress
=
({
hash
}:
Props
)
=>
{
const
TokenInstanceCreatorAddress
=
({
hash
}:
Props
)
=>
{
const
addressQuery
=
useApiQuery
(
'
address
'
,
{
const
addressQuery
=
useApiQuery
(
'
address
'
,
{
pathParams
:
{
hash
},
pathParams
:
{
hash
},
queryOptions
:
{
enabled
:
Boolean
(
hash
),
placeholderData
:
ADDRESS_INFO
,
},
});
});
if
(
addressQuery
.
isError
)
{
if
(
addressQuery
.
isError
)
{
return
null
;
return
null
;
}
}
if
(
addressQuery
.
isLoading
)
{
if
(
!
addressQuery
.
data
?.
creator_address_hash
)
{
return
<
DetailsSkeletonRow
w=
"30%"
/>;
}
if
(
!
addressQuery
.
data
.
creator_address_hash
)
{
return
null
;
return
null
;
}
}
...
@@ -39,11 +39,12 @@ const TokenInstanceCreatorAddress = ({ hash }: Props) => {
...
@@ -39,11 +39,12 @@ const TokenInstanceCreatorAddress = ({ hash }: Props) => {
<
DetailsInfoItem
<
DetailsInfoItem
title=
"Creator"
title=
"Creator"
hint=
"Address that deployed this token contract"
hint=
"Address that deployed this token contract"
isLoading=
{
addressQuery
.
isPlaceholderData
}
>
>
<
Address
>
<
Address
>
<
AddressIcon
address=
{
creatorAddress
}
/>
<
AddressIcon
address=
{
creatorAddress
}
isLoading=
{
addressQuery
.
isPlaceholderData
}
/>
<
AddressLink
type=
"address"
hash=
{
creatorAddress
.
hash
}
ml=
{
2
}
/>
<
AddressLink
type=
"address"
hash=
{
creatorAddress
.
hash
}
ml=
{
2
}
isLoading=
{
addressQuery
.
isPlaceholderData
}
/>
<
CopyToClipboard
text=
{
creatorAddress
.
hash
}
/>
<
CopyToClipboard
text=
{
creatorAddress
.
hash
}
isLoading=
{
addressQuery
.
isPlaceholderData
}
/>
</
Address
>
</
Address
>
</
DetailsInfoItem
>
</
DetailsInfoItem
>
);
);
...
...
ui/tokenInstance/details/TokenInstanceTransfersCount.tsx
View file @
70d95e92
import
{
Skeleton
}
from
'
@chakra-ui/react
'
;
import
{
route
}
from
'
nextjs-routes
'
;
import
{
route
}
from
'
nextjs-routes
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
DetailsInfoItem
from
'
ui/shared/DetailsInfoItem
'
;
import
DetailsInfoItem
from
'
ui/shared/DetailsInfoItem
'
;
import
LinkInternal
from
'
ui/shared/LinkInternal
'
;
import
LinkInternal
from
'
ui/shared/LinkInternal
'
;
import
DetailsSkeletonRow
from
'
ui/shared/skeletons/DetailsSkeletonRow
'
;
interface
Props
{
interface
Props
{
hash
:
string
;
hash
:
string
;
...
@@ -15,17 +15,19 @@ interface Props {
...
@@ -15,17 +15,19 @@ interface Props {
const
TokenInstanceTransfersCount
=
({
hash
,
id
,
onClick
}:
Props
)
=>
{
const
TokenInstanceTransfersCount
=
({
hash
,
id
,
onClick
}:
Props
)
=>
{
const
transfersCountQuery
=
useApiQuery
(
'
token_instance_transfers_count
'
,
{
const
transfersCountQuery
=
useApiQuery
(
'
token_instance_transfers_count
'
,
{
pathParams
:
{
hash
,
id
},
pathParams
:
{
hash
,
id
},
queryOptions
:
{
enabled
:
Boolean
(
hash
&&
id
),
placeholderData
:
{
transfers_count
:
420
,
},
},
});
});
if
(
transfersCountQuery
.
isError
)
{
if
(
transfersCountQuery
.
isError
)
{
return
null
;
return
null
;
}
}
if
(
transfersCountQuery
.
isLoading
)
{
if
(
!
transfersCountQuery
.
data
?.
transfers_count
)
{
return
<
DetailsSkeletonRow
w=
"30%"
/>;
}
if
(
!
transfersCountQuery
.
data
.
transfers_count
)
{
return
null
;
return
null
;
}
}
...
@@ -37,13 +39,16 @@ const TokenInstanceTransfersCount = ({ hash, id, onClick }: Props) => {
...
@@ -37,13 +39,16 @@ const TokenInstanceTransfersCount = ({ hash, id, onClick }: Props) => {
<
DetailsInfoItem
<
DetailsInfoItem
title=
"Transfers"
title=
"Transfers"
hint=
"Number of transfer for the token instance"
hint=
"Number of transfer for the token instance"
isLoading=
{
transfersCountQuery
.
isPlaceholderData
}
>
>
<
LinkInternal
<
Skeleton
isLoaded=
{
!
transfersCountQuery
.
isPlaceholderData
}
display=
"inline-block"
>
href=
{
url
}
<
LinkInternal
onClick=
{
transfersCountQuery
.
data
.
transfers_count
>
0
?
onClick
:
undefined
}
href=
{
url
}
>
onClick=
{
transfersCountQuery
.
data
.
transfers_count
>
0
?
onClick
:
undefined
}
{
transfersCountQuery
.
data
.
transfers_count
.
toLocaleString
()
}
>
</
LinkInternal
>
{
transfersCountQuery
.
data
.
transfers_count
.
toLocaleString
()
}
</
LinkInternal
>
</
Skeleton
>
</
DetailsInfoItem
>
</
DetailsInfoItem
>
);
);
};
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment