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
88db5a36
Unverified
Commit
88db5a36
authored
Dec 15, 2022
by
tom goriunov
Committed by
GitHub
Dec 15, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #433 from blockscout/address-blocks-validated
address page: blocks validated
parents
9e0fd204
6fc40192
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
347 additions
and
21 deletions
+347
-21
blocks-validated.ts
pages/api/addresses/[id]/blocks-validated.ts
+13
-0
address.ts
types/api/address.ts
+9
-0
pagination.ts
types/api/pagination.ts
+7
-2
queries.ts
types/client/queries.ts
+1
-0
AddressBlocksValidated.tsx
ui/address/AddressBlocksValidated.tsx
+140
-0
AddressBlocksValidatedListItem.tsx
...ddress/blocksValidated/AddressBlocksValidatedListItem.tsx
+46
-0
AddressBlocksValidatedSkeletonMobile.tsx
.../blocksValidated/AddressBlocksValidatedSkeletonMobile.tsx
+43
-0
AddressBlocksValidatedTableItem.tsx
...dress/blocksValidated/AddressBlocksValidatedTableItem.tsx
+45
-0
AddressCoinBalanceListItem.tsx
ui/address/coinBalance/AddressCoinBalanceListItem.tsx
+1
-1
Address.tsx
ui/pages/Address.tsx
+4
-0
RoutedTabs.tsx
ui/shared/RoutedTabs/RoutedTabs.tsx
+32
-13
useAdaptiveTabs.tsx
ui/shared/RoutedTabs/useAdaptiveTabs.tsx
+6
-5
No files found.
pages/api/addresses/[id]/blocks-validated.ts
0 → 100644
View file @
88db5a36
import
type
{
NextApiRequest
}
from
'
next
'
;
import
getSearchParams
from
'
lib/api/getSearchParams
'
;
import
handler
from
'
lib/api/handler
'
;
const
getUrl
=
(
req
:
NextApiRequest
)
=>
{
const
searchParamsStr
=
getSearchParams
(
req
);
return
`/v2/addresses/
${
req
.
query
.
id
}
/blocks-validated
${
searchParamsStr
?
'
?
'
+
searchParamsStr
:
''
}
`
;
};
const
requestHandler
=
handler
(
getUrl
,
[
'
GET
'
]);
export
default
requestHandler
;
types/api/address.ts
View file @
88db5a36
import
type
{
Transaction
}
from
'
types/api/transaction
'
;
import
type
{
Transaction
}
from
'
types/api/transaction
'
;
import
type
{
AddressTag
,
WatchlistName
}
from
'
./addressParams
'
;
import
type
{
AddressTag
,
WatchlistName
}
from
'
./addressParams
'
;
import
type
{
Block
}
from
'
./block
'
;
import
type
{
TokenInfo
,
TokenType
}
from
'
./tokenInfo
'
;
import
type
{
TokenInfo
,
TokenType
}
from
'
./tokenInfo
'
;
import
type
{
TokenTransfer
,
TokenTransferPagination
}
from
'
./tokenTransfer
'
;
import
type
{
TokenTransfer
,
TokenTransferPagination
}
from
'
./tokenTransfer
'
;
...
@@ -75,3 +76,11 @@ export interface AddressCoinBalanceHistoryResponse {
...
@@ -75,3 +76,11 @@ export interface AddressCoinBalanceHistoryResponse {
items_count
:
number
;
items_count
:
number
;
};
};
}
}
export
interface
AddressBlocksValidatedResponse
{
items
:
Array
<
Block
>
;
next_page_params
:
{
block_number
:
number
;
items_count
:
number
;
};
}
types/api/pagination.ts
View file @
88db5a36
...
@@ -4,6 +4,7 @@ import type {
...
@@ -4,6 +4,7 @@ import type {
AddressTxsFilters
,
AddressTxsFilters
,
AddressTokenTransferFilters
,
AddressTokenTransferFilters
,
AddressCoinBalanceHistoryResponse
,
AddressCoinBalanceHistoryResponse
,
AddressBlocksValidatedResponse
,
}
from
'
types/api/address
'
;
}
from
'
types/api/address
'
;
import
type
{
BlocksResponse
,
BlockTransactionsResponse
,
BlockFilters
}
from
'
types/api/block
'
;
import
type
{
BlocksResponse
,
BlockTransactionsResponse
,
BlockFilters
}
from
'
types/api/block
'
;
import
type
{
InternalTransactionsResponse
}
from
'
types/api/internalTransaction
'
;
import
type
{
InternalTransactionsResponse
}
from
'
types/api/internalTransaction
'
;
...
@@ -26,7 +27,8 @@ export type PaginatedQueryKeys =
...
@@ -26,7 +27,8 @@ export type PaginatedQueryKeys =
QueryKeys
.
txInternals
|
QueryKeys
.
txInternals
|
QueryKeys
.
txLogs
|
QueryKeys
.
txLogs
|
QueryKeys
.
txTokenTransfers
|
QueryKeys
.
txTokenTransfers
|
QueryKeys
.
addressCoinBalanceHistory
;
QueryKeys
.
addressCoinBalanceHistory
|
QueryKeys
.
addressBlocksValidated
;
export
type
PaginatedResponse
<
Q
extends
PaginatedQueryKeys
>
=
export
type
PaginatedResponse
<
Q
extends
PaginatedQueryKeys
>
=
Q
extends
QueryKeys
.
addressTxs
?
AddressTransactionsResponse
:
Q
extends
QueryKeys
.
addressTxs
?
AddressTransactionsResponse
:
...
@@ -39,7 +41,8 @@ export type PaginatedResponse<Q extends PaginatedQueryKeys> =
...
@@ -39,7 +41,8 @@ export type PaginatedResponse<Q extends PaginatedQueryKeys> =
Q
extends
QueryKeys
.
txLogs
?
LogsResponse
:
Q
extends
QueryKeys
.
txLogs
?
LogsResponse
:
Q
extends
QueryKeys
.
txTokenTransfers
?
TokenTransferResponse
:
Q
extends
QueryKeys
.
txTokenTransfers
?
TokenTransferResponse
:
Q
extends
QueryKeys
.
addressCoinBalanceHistory
?
AddressCoinBalanceHistoryResponse
:
Q
extends
QueryKeys
.
addressCoinBalanceHistory
?
AddressCoinBalanceHistoryResponse
:
never
Q
extends
QueryKeys
.
addressBlocksValidated
?
AddressBlocksValidatedResponse
:
never
export
type
PaginationFilters
<
Q
extends
PaginatedQueryKeys
>
=
export
type
PaginationFilters
<
Q
extends
PaginatedQueryKeys
>
=
Q
extends
QueryKeys
.
addressTxs
?
AddressTxsFilters
:
Q
extends
QueryKeys
.
addressTxs
?
AddressTxsFilters
:
...
@@ -69,6 +72,7 @@ export const PAGINATION_FIELDS: PaginationFields = {
...
@@ -69,6 +72,7 @@ export const PAGINATION_FIELDS: PaginationFields = {
[
QueryKeys
.
txTokenTransfers
]:
[
'
block_number
'
,
'
items_count
'
,
'
transaction_hash
'
,
'
index
'
],
[
QueryKeys
.
txTokenTransfers
]:
[
'
block_number
'
,
'
items_count
'
,
'
transaction_hash
'
,
'
index
'
],
[
QueryKeys
.
txLogs
]:
[
'
items_count
'
,
'
transaction_hash
'
,
'
index
'
],
[
QueryKeys
.
txLogs
]:
[
'
items_count
'
,
'
transaction_hash
'
,
'
index
'
],
[
QueryKeys
.
addressCoinBalanceHistory
]:
[
'
items_count
'
,
'
block_number
'
],
[
QueryKeys
.
addressCoinBalanceHistory
]:
[
'
items_count
'
,
'
block_number
'
],
[
QueryKeys
.
addressBlocksValidated
]:
[
'
items_count
'
,
'
block_number
'
],
};
};
type
PaginationFiltersFields
=
{
type
PaginationFiltersFields
=
{
...
@@ -79,6 +83,7 @@ export const PAGINATION_FILTERS_FIELDS: PaginationFiltersFields = {
...
@@ -79,6 +83,7 @@ export const PAGINATION_FILTERS_FIELDS: PaginationFiltersFields = {
[
QueryKeys
.
addressTxs
]:
[
'
filter
'
],
[
QueryKeys
.
addressTxs
]:
[
'
filter
'
],
[
QueryKeys
.
addressTokenTransfers
]:
[
'
filter
'
,
'
type
'
],
[
QueryKeys
.
addressTokenTransfers
]:
[
'
filter
'
,
'
type
'
],
[
QueryKeys
.
addressCoinBalanceHistory
]:
[],
[
QueryKeys
.
addressCoinBalanceHistory
]:
[],
[
QueryKeys
.
addressBlocksValidated
]:
[],
[
QueryKeys
.
blocks
]:
[
'
type
'
],
[
QueryKeys
.
blocks
]:
[
'
type
'
],
[
QueryKeys
.
txsValidate
]:
[
'
filter
'
,
'
type
'
,
'
method
'
],
[
QueryKeys
.
txsValidate
]:
[
'
filter
'
,
'
type
'
,
'
method
'
],
[
QueryKeys
.
txsPending
]:
[
'
filter
'
,
'
type
'
,
'
method
'
],
[
QueryKeys
.
txsPending
]:
[
'
filter
'
,
'
type
'
,
'
method
'
],
...
...
types/client/queries.ts
View file @
88db5a36
...
@@ -28,4 +28,5 @@ export enum QueryKeys {
...
@@ -28,4 +28,5 @@ export enum QueryKeys {
addressCoinBalanceHistoryByDay
=
'
address-coin-balance-history-by-day
'
,
addressCoinBalanceHistoryByDay
=
'
address-coin-balance-history-by-day
'
,
addressTxs
=
'
addressTxs
'
,
addressTxs
=
'
addressTxs
'
,
addressTokenTransfers
=
'
addressTokenTransfers
'
,
addressTokenTransfers
=
'
addressTokenTransfers
'
,
addressBlocksValidated
=
'
address-blocks-validated
'
,
}
}
ui/address/AddressBlocksValidated.tsx
0 → 100644
View file @
88db5a36
import
{
Box
,
Hide
,
Show
,
Table
,
Tbody
,
Th
,
Tr
}
from
'
@chakra-ui/react
'
;
import
{
useQueryClient
}
from
'
@tanstack/react-query
'
;
import
type
{
UseQueryResult
}
from
'
@tanstack/react-query
'
;
import
React
from
'
react
'
;
import
type
{
SocketMessage
}
from
'
lib/socket/types
'
;
import
type
{
Address
,
AddressBlocksValidatedResponse
}
from
'
types/api/address
'
;
import
{
QueryKeys
}
from
'
types/client/queries
'
;
import
appConfig
from
'
configs/app/config
'
;
import
useQueryWithPages
from
'
lib/hooks/useQueryWithPages
'
;
import
useSocketChannel
from
'
lib/socket/useSocketChannel
'
;
import
useSocketMessage
from
'
lib/socket/useSocketMessage
'
;
import
ActionBar
from
'
ui/shared/ActionBar
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
Pagination
from
'
ui/shared/Pagination
'
;
import
SkeletonTable
from
'
ui/shared/SkeletonTable
'
;
import
SocketAlert
from
'
ui/shared/SocketAlert
'
;
import
{
default
as
Thead
}
from
'
ui/shared/TheadSticky
'
;
import
AddressBlocksValidatedListItem
from
'
./blocksValidated/AddressBlocksValidatedListItem
'
;
import
AddressBlocksValidatedSkeletonMobile
from
'
./blocksValidated/AddressBlocksValidatedSkeletonMobile
'
;
import
AddressBlocksValidatedTableItem
from
'
./blocksValidated/AddressBlocksValidatedTableItem
'
;
interface
Props
{
addressQuery
:
UseQueryResult
<
Address
>
;
}
const
AddressBlocksValidated
=
({
addressQuery
}:
Props
)
=>
{
const
[
socketAlert
,
setSocketAlert
]
=
React
.
useState
(
false
);
const
queryClient
=
useQueryClient
();
const
query
=
useQueryWithPages
({
apiPath
:
`/node-api/addresses/
${
addressQuery
.
data
?.
hash
}
/blocks-validated`
,
queryName
:
QueryKeys
.
addressBlocksValidated
,
options
:
{
enabled
:
Boolean
(
addressQuery
.
data
),
}
,
});
const handleSocketError = React.useCallback(() => {
setSocketAlert(true);
}, []);
const handleNewSocketMessage: SocketMessage.NewBlock['handler'] = React.useCallback((payload) => {
setSocketAlert(false);
queryClient.setQueryData(
[ QueryKeys.addressBlocksValidated, { page: query.pagination.page } ],
(prevData: AddressBlocksValidatedResponse | undefined) => {
if (!prevData) {
return;
}
return {
...prevData,
items: [ payload.block, ...prevData.items ],
};
});
}, [ query.pagination.page, queryClient ]);
const channel = useSocketChannel({
topic: `
blocks
:
$
{
addressQuery
.
data
?.
hash
.
toLowerCase
()
}
`,
onSocketClose: handleSocketError,
onSocketError: handleSocketError,
isDisabled: addressQuery.isLoading || addressQuery.isError || !addressQuery.data.hash || query.pagination.page !== 1,
});
useSocketMessage({
channel,
event: 'new_block',
handler: handleNewSocketMessage,
});
const content = (() => {
if (query.isLoading) {
return (
<>
<Hide below="lg">
<SkeletonTable columns={ [ '17%', '17%', '16%', '25%', '25%' ] }/>
</Hide>
<Show below="lg">
<AddressBlocksValidatedSkeletonMobile/>
</Show>
</>
);
}
if (query.isError) {
return <DataFetchAlert/>;
}
if (query.data.items.length === 0) {
return 'There is no validated blocks for this address';
}
return (
<>
<Hide below="lg">
<Table variant="simple" size="sm">
<Thead top={ 80 }>
<Tr>
<Th width="17%">Block</Th>
<Th width="17%">Age</Th>
<Th width="16%">Txn</Th>
<Th width="25%">GasUsed</Th>
<Th width="25%" isNumeric>Reward { appConfig.network.currency.symbol }</Th>
</Tr>
</Thead>
<Tbody>
{ query.data.items.map((item) => (
<AddressBlocksValidatedTableItem key={ item.height } { ...item } page={ query.pagination.page }/>
)) }
</Tbody>
</Table>
</Hide>
<Show below="lg">
{ query.data.items.map((item) => (
<AddressBlocksValidatedListItem key={ item.height } { ...item } page={ query.pagination.page }/>
)) }
</Show>
</>
);
})();
const isPaginatorHidden = !query.isLoading && !query.isError && query.pagination.page === 1 && !query.pagination.hasNextPage;
return (
<Box>
{ !isPaginatorHidden && (
<ActionBar mt={ -6 }>
<Pagination ml="auto" { ...query.pagination }/>
</ActionBar>
) }
{ socketAlert && <SocketAlert mb={ 6 }/> }
{ content }
</Box>
);
};
export default React.memo(AddressBlocksValidated);
ui/address/blocksValidated/AddressBlocksValidatedListItem.tsx
0 → 100644
View file @
88db5a36
import
{
Link
,
Text
,
Flex
}
from
'
@chakra-ui/react
'
;
import
BigNumber
from
'
bignumber.js
'
;
import
React
from
'
react
'
;
import
type
{
Block
}
from
'
types/api/block
'
;
import
appConfig
from
'
configs/app/config
'
;
import
getBlockTotalReward
from
'
lib/block/getBlockTotalReward
'
;
import
useTimeAgoIncrement
from
'
lib/hooks/useTimeAgoIncrement
'
;
import
link
from
'
lib/link/link
'
;
import
AccountListItemMobile
from
'
ui/shared/AccountListItemMobile
'
;
import
Utilization
from
'
ui/shared/Utilization/Utilization
'
;
type
Props
=
Block
&
{
page
:
number
;
};
const
AddressBlocksValidatedListItem
=
(
props
:
Props
)
=>
{
const
blockUrl
=
link
(
'
block
'
,
{
id
:
String
(
props
.
height
)
});
const
timeAgo
=
useTimeAgoIncrement
(
props
.
timestamp
,
props
.
page
===
1
);
const
totalReward
=
getBlockTotalReward
(
props
);
return
(
<
AccountListItemMobile
rowGap=
{
2
}
>
<
Flex
justifyContent=
"space-between"
w=
"100%"
>
<
Link
href=
{
blockUrl
}
fontWeight=
"700"
>
{
props
.
height
}
</
Link
>
<
Text
variant=
"secondary"
>
{
timeAgo
}
</
Text
>
</
Flex
>
<
Flex
columnGap=
{
2
}
w=
"100%"
>
<
Text
fontWeight=
{
500
}
flexShrink=
{
0
}
>
Txn
</
Text
>
<
Text
variant=
"secondary"
>
{
props
.
tx_count
}
</
Text
>
</
Flex
>
<
Flex
columnGap=
{
2
}
w=
"100%"
>
<
Text
fontWeight=
{
500
}
flexShrink=
{
0
}
>
Gas used
</
Text
>
<
Text
variant=
"secondary"
>
{
BigNumber
(
props
.
gas_used
||
0
).
toFormat
()
}
</
Text
>
<
Utilization
colorScheme=
"gray"
value=
{
BigNumber
(
props
.
gas_used
||
0
).
dividedBy
(
BigNumber
(
props
.
gas_limit
)).
toNumber
()
}
/>
</
Flex
>
<
Flex
columnGap=
{
2
}
w=
"100%"
>
<
Text
fontWeight=
{
500
}
flexShrink=
{
0
}
>
Reward
{
appConfig
.
network
.
currency
.
symbol
}
</
Text
>
<
Text
variant=
"secondary"
>
{
totalReward
}
</
Text
>
</
Flex
>
</
AccountListItemMobile
>
);
};
export
default
React
.
memo
(
AddressBlocksValidatedListItem
);
ui/address/blocksValidated/AddressBlocksValidatedSkeletonMobile.tsx
0 → 100644
View file @
88db5a36
import
{
Skeleton
,
Flex
,
Box
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
const
AddressBlocksValidatedSkeletonMobile
=
()
=>
{
const
borderColor
=
useColorModeValue
(
'
blackAlpha.200
'
,
'
whiteAlpha.200
'
);
return
(
<
Box
>
{
Array
.
from
(
Array
(
2
)).
map
((
item
,
index
)
=>
(
<
Flex
key=
{
index
}
rowGap=
{
3
}
flexDirection=
"column"
paddingY=
{
6
}
borderTopWidth=
"1px"
borderColor=
{
borderColor
}
_last=
{
{
borderBottomWidth
:
'
1px
'
,
}
}
>
<
Flex
justifyContent=
"space-between"
w=
"100%"
h=
{
6
}
>
<
Skeleton
w=
"100px"
/>
<
Skeleton
w=
"100px"
/>
</
Flex
>
<
Flex
h=
{
6
}
columnGap=
{
2
}
>
<
Skeleton
w=
"40px"
/>
<
Skeleton
w=
"40px"
/>
</
Flex
>
<
Flex
h=
{
6
}
columnGap=
{
2
}
>
<
Skeleton
w=
"70px"
/>
<
Skeleton
w=
"70px"
/>
</
Flex
>
<
Flex
h=
{
6
}
columnGap=
{
2
}
>
<
Skeleton
w=
"100px"
/>
<
Skeleton
w=
"120px"
/>
</
Flex
>
</
Flex
>
))
}
</
Box
>
);
};
export
default
AddressBlocksValidatedSkeletonMobile
;
ui/address/blocksValidated/AddressBlocksValidatedTableItem.tsx
0 → 100644
View file @
88db5a36
import
{
Link
,
Td
,
Tr
,
Text
,
Box
,
Flex
}
from
'
@chakra-ui/react
'
;
import
BigNumber
from
'
bignumber.js
'
;
import
React
from
'
react
'
;
import
type
{
Block
}
from
'
types/api/block
'
;
import
getBlockTotalReward
from
'
lib/block/getBlockTotalReward
'
;
import
useTimeAgoIncrement
from
'
lib/hooks/useTimeAgoIncrement
'
;
import
link
from
'
lib/link/link
'
;
import
Utilization
from
'
ui/shared/Utilization/Utilization
'
;
type
Props
=
Block
&
{
page
:
number
;
};
const
AddressBlocksValidatedTableItem
=
(
props
:
Props
)
=>
{
const
blockUrl
=
link
(
'
block
'
,
{
id
:
String
(
props
.
height
)
});
const
timeAgo
=
useTimeAgoIncrement
(
props
.
timestamp
,
props
.
page
===
1
);
const
totalReward
=
getBlockTotalReward
(
props
);
return
(
<
Tr
>
<
Td
>
<
Link
href=
{
blockUrl
}
fontWeight=
"700"
>
{
props
.
height
}
</
Link
>
</
Td
>
<
Td
>
<
Text
variant=
"secondary"
>
{
timeAgo
}
</
Text
>
</
Td
>
<
Td
>
<
Text
fontWeight=
"500"
>
{
props
.
tx_count
}
</
Text
>
</
Td
>
<
Td
>
<
Flex
alignItems=
"center"
columnGap=
{
2
}
>
<
Box
flexBasis=
"80px"
>
{
BigNumber
(
props
.
gas_used
||
0
).
toFormat
()
}
</
Box
>
<
Utilization
colorScheme=
"gray"
value=
{
BigNumber
(
props
.
gas_used
||
0
).
dividedBy
(
BigNumber
(
props
.
gas_limit
)).
toNumber
()
}
/>
</
Flex
>
</
Td
>
<
Td
isNumeric
display=
"flex"
justifyContent=
"end"
>
{
totalReward
}
</
Td
>
</
Tr
>
);
};
export
default
React
.
memo
(
AddressBlocksValidatedTableItem
);
ui/address/coinBalance/AddressCoinBalanceListItem.tsx
View file @
88db5a36
...
@@ -23,7 +23,7 @@ const AddressCoinBalanceListItem = (props: Props) => {
...
@@ -23,7 +23,7 @@ const AddressCoinBalanceListItem = (props: Props) => {
const
timeAgo
=
useTimeAgoIncrement
(
props
.
block_timestamp
,
props
.
page
===
1
);
const
timeAgo
=
useTimeAgoIncrement
(
props
.
block_timestamp
,
props
.
page
===
1
);
return
(
return
(
<
AccountListItemMobile
fontSize=
"sm"
rowGap=
{
2
}
>
<
AccountListItemMobile
rowGap=
{
2
}
>
<
Flex
justifyContent=
"space-between"
w=
"100%"
>
<
Flex
justifyContent=
"space-between"
w=
"100%"
>
<
Text
fontWeight=
{
600
}
>
{
BigNumber
(
props
.
value
).
div
(
WEI
).
toFixed
(
8
)
}
{
appConfig
.
network
.
currency
.
symbol
}
</
Text
>
<
Text
fontWeight=
{
600
}
>
{
BigNumber
(
props
.
value
).
div
(
WEI
).
toFixed
(
8
)
}
{
appConfig
.
network
.
currency
.
symbol
}
</
Text
>
<
Stat
flexGrow=
"0"
>
<
Stat
flexGrow=
"0"
>
...
...
ui/pages/Address.tsx
View file @
88db5a36
...
@@ -8,6 +8,7 @@ import { QueryKeys } from 'types/client/queries';
...
@@ -8,6 +8,7 @@ import { QueryKeys } from 'types/client/queries';
import
type
{
RoutedTab
}
from
'
ui/shared/RoutedTabs/types
'
;
import
type
{
RoutedTab
}
from
'
ui/shared/RoutedTabs/types
'
;
import
useFetch
from
'
lib/hooks/useFetch
'
;
import
useFetch
from
'
lib/hooks/useFetch
'
;
import
AddressBlocksValidated
from
'
ui/address/AddressBlocksValidated
'
;
import
AddressCoinBalance
from
'
ui/address/AddressCoinBalance
'
;
import
AddressCoinBalance
from
'
ui/address/AddressCoinBalance
'
;
import
AddressDetails
from
'
ui/address/AddressDetails
'
;
import
AddressDetails
from
'
ui/address/AddressDetails
'
;
import
AddressTxs
from
'
ui/address/AddressTxs
'
;
import
AddressTxs
from
'
ui/address/AddressTxs
'
;
...
@@ -39,6 +40,9 @@ const AddressPageContent = () => {
...
@@ -39,6 +40,9 @@ const AddressPageContent = () => {
{
id
:
'
tokens
'
,
title
:
'
Tokens
'
,
component
:
null
},
{
id
:
'
tokens
'
,
title
:
'
Tokens
'
,
component
:
null
},
{
id
:
'
internal_txn
'
,
title
:
'
Internal txn
'
,
component
:
null
},
{
id
:
'
internal_txn
'
,
title
:
'
Internal txn
'
,
component
:
null
},
{
id
:
'
coin_balance_history
'
,
title
:
'
Coin balance history
'
,
component
:
<
AddressCoinBalance
addressQuery=
{
addressQuery
}
/>
},
{
id
:
'
coin_balance_history
'
,
title
:
'
Coin balance history
'
,
component
:
<
AddressCoinBalance
addressQuery=
{
addressQuery
}
/>
},
// temporary show this tab in all address
// later api will return info about available tabs
{
id
:
'
blocks_validated
'
,
title
:
'
Blocks validated
'
,
component
:
<
AddressBlocksValidated
addressQuery=
{
addressQuery
}
/>
},
];
];
return
(
return
(
...
...
ui/shared/RoutedTabs/RoutedTabs.tsx
View file @
88db5a36
...
@@ -40,19 +40,6 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
...
@@ -40,19 +40,6 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
const
scrollDirection
=
useScrollDirection
();
const
scrollDirection
=
useScrollDirection
();
const
[
activeTabIndex
,
setActiveTabIndex
]
=
useState
<
number
>
(
tabs
.
length
+
1
);
const
[
activeTabIndex
,
setActiveTabIndex
]
=
useState
<
number
>
(
tabs
.
length
+
1
);
useEffect
(()
=>
{
if
(
router
.
isReady
)
{
let
tabIndex
=
0
;
if
(
router
.
query
.
tab
)
{
tabIndex
=
tabs
.
findIndex
(({
id
})
=>
id
===
router
.
query
.
tab
);
if
(
tabIndex
<
0
)
{
tabIndex
=
0
;
}
}
setActiveTabIndex
(
tabIndex
);
}
},
[
tabs
,
router
]);
const
isMobile
=
useIsMobile
();
const
isMobile
=
useIsMobile
();
const
{
tabsCut
,
tabsList
,
tabsRefs
,
listRef
,
rightSlotRef
}
=
useAdaptiveTabs
(
tabs
,
isMobile
);
const
{
tabsCut
,
tabsList
,
tabsRefs
,
listRef
,
rightSlotRef
}
=
useAdaptiveTabs
(
tabs
,
isMobile
);
const
isSticky
=
useIsSticky
(
listRef
,
5
,
stickyEnabled
);
const
isSticky
=
useIsSticky
(
listRef
,
5
,
stickyEnabled
);
...
@@ -68,6 +55,38 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
...
@@ -68,6 +55,38 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
);
);
},
[
tabs
,
router
]);
},
[
tabs
,
router
]);
useEffect
(()
=>
{
if
(
router
.
isReady
)
{
let
tabIndex
=
0
;
if
(
router
.
query
.
tab
)
{
tabIndex
=
tabs
.
findIndex
(({
id
})
=>
id
===
router
.
query
.
tab
);
if
(
tabIndex
<
0
)
{
tabIndex
=
0
;
}
}
setActiveTabIndex
(
tabIndex
);
}
},
[
tabs
,
router
,
activeTabIndex
]);
useEffect
(()
=>
{
if
(
activeTabIndex
<
tabs
.
length
&&
isMobile
)
{
window
.
setTimeout
(()
=>
{
const
activeTabRef
=
tabsRefs
[
activeTabIndex
];
if
(
activeTabRef
.
current
&&
listRef
.
current
)
{
const
activeTabRect
=
activeTabRef
.
current
.
getBoundingClientRect
();
listRef
.
current
.
scrollTo
({
left
:
activeTabRect
.
left
+
listRef
.
current
.
scrollLeft
-
16
,
behavior
:
'
smooth
'
,
});
}
// have to wait until DOM is updated and all styles to tabs is applied
},
300
);
}
// run only when tab index or device type is updated
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
activeTabIndex
,
isMobile
]);
return
(
return
(
<
Tabs
<
Tabs
variant=
"soft-rounded"
variant=
"soft-rounded"
...
...
ui/shared/RoutedTabs/useAdaptiveTabs.tsx
View file @
88db5a36
...
@@ -51,19 +51,20 @@ export default function useAdaptiveTabs(tabs: Array<RoutedTab>, disabled?: boole
...
@@ -51,19 +51,20 @@ export default function useAdaptiveTabs(tabs: Array<RoutedTab>, disabled?: boole
},
[
tabs
,
disabled
]);
},
[
tabs
,
disabled
]);
React
.
useEffect
(()
=>
{
React
.
useEffect
(()
=>
{
setTabsRefs
(
disabled
?
[]
:
tabsList
.
map
((
_
,
index
)
=>
tabsRefs
[
index
]
||
React
.
createRef
()));
setTabsRefs
(
tabsList
.
map
((
_
,
index
)
=>
tabsRefs
[
index
]
||
React
.
createRef
()));
setTabsCut
(
disabled
?
tabs
.
length
:
0
);
// update refs only when disabled prop changes
// update refs only when disabled prop changes
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
disabled
]);
},
[
disabled
]);
React
.
useEffect
(()
=>
{
React
.
useEffect
(()
=>
{
if
(
tabsRefs
.
length
>
0
)
{
if
(
tabsRefs
.
length
>
0
&&
!
disabled
)
{
setTabsCut
(
calculateCut
());
setTabsCut
(
calculateCut
());
}
}
},
[
calculateCut
,
tabsRefs
]);
},
[
calculateCut
,
disabled
,
tabsRefs
]);
React
.
useEffect
(()
=>
{
React
.
useEffect
(()
=>
{
if
(
tabsRefs
.
length
===
0
)
{
if
(
tabsRefs
.
length
===
0
||
disabled
)
{
return
;
return
;
}
}
...
@@ -76,7 +77,7 @@ export default function useAdaptiveTabs(tabs: Array<RoutedTab>, disabled?: boole
...
@@ -76,7 +77,7 @@ export default function useAdaptiveTabs(tabs: Array<RoutedTab>, disabled?: boole
return
function
cleanup
()
{
return
function
cleanup
()
{
resizeObserver
.
unobserve
(
document
.
body
);
resizeObserver
.
unobserve
(
document
.
body
);
};
};
},
[
calculateCut
,
tabsRefs
.
length
]);
},
[
calculateCut
,
disabled
,
tabsRefs
.
length
]);
return
React
.
useMemo
(()
=>
{
return
React
.
useMemo
(()
=>
{
return
{
return
{
...
...
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