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
064c44d6
Commit
064c44d6
authored
Aug 28, 2023
by
isstuev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add counts to address tabs
parent
1c21cfe3
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
181 additions
and
16 deletions
+181
-16
resources.ts
lib/api/resources.ts
+6
-0
address.ts
stubs/address.ts
+12
-1
address.ts
types/api/address.ts
+12
-0
Address.tsx
ui/pages/Address.tsx
+62
-15
TabCounter.tsx
ui/shared/Tabs/TabCounter.tsx
+37
-0
TabsMenu.tsx
ui/shared/Tabs/TabsMenu.tsx
+5
-0
TabsWithScroll.pw.tsx
ui/shared/Tabs/TabsWithScroll.pw.tsx
+40
-0
TabsWithScroll.tsx
ui/shared/Tabs/TabsWithScroll.tsx
+5
-0
TabsWithScroll.pw.tsx_default_with-counters-1.png
...shots__/TabsWithScroll.pw.tsx_default_with-counters-1.png
+0
-0
types.ts
ui/shared/Tabs/types.ts
+2
-0
No files found.
lib/api/resources.ts
View file @
064c44d6
...
@@ -14,6 +14,7 @@ import type {
...
@@ -14,6 +14,7 @@ import type {
import
type
{
import
type
{
Address
,
Address
,
AddressCounters
,
AddressCounters
,
AddressTabsCounters
,
AddressTransactionsResponse
,
AddressTransactionsResponse
,
AddressTokenTransferResponse
,
AddressTokenTransferResponse
,
AddressCoinBalanceHistoryResponse
,
AddressCoinBalanceHistoryResponse
,
...
@@ -248,6 +249,10 @@ export const RESOURCES = {
...
@@ -248,6 +249,10 @@ export const RESOURCES = {
path
:
'
/api/v2/addresses/:hash/counters
'
,
path
:
'
/api/v2/addresses/:hash/counters
'
,
pathParams
:
[
'
hash
'
as
const
],
pathParams
:
[
'
hash
'
as
const
],
},
},
address_tabs_counters
:
{
path
:
'
/api/v2/addresses/:hash/tabs-counters
'
,
pathParams
:
[
'
hash
'
as
const
],
},
// this resource doesn't have pagination, so causing huge problems on some addresses page
// this resource doesn't have pagination, so causing huge problems on some addresses page
// address_token_balances: {
// address_token_balances: {
// path: '/api/v2/addresses/:hash/token-balances',
// path: '/api/v2/addresses/:hash/token-balances',
...
@@ -577,6 +582,7 @@ Q extends 'tx_state_changes' ? TxStateChanges :
...
@@ -577,6 +582,7 @@ Q extends 'tx_state_changes' ? TxStateChanges :
Q
extends
'
addresses
'
?
AddressesResponse
:
Q
extends
'
addresses
'
?
AddressesResponse
:
Q
extends
'
address
'
?
Address
:
Q
extends
'
address
'
?
Address
:
Q
extends
'
address_counters
'
?
AddressCounters
:
Q
extends
'
address_counters
'
?
AddressCounters
:
Q
extends
'
address_tabs_counters
'
?
AddressTabsCounters
:
Q
extends
'
address_txs
'
?
AddressTransactionsResponse
:
Q
extends
'
address_txs
'
?
AddressTransactionsResponse
:
Q
extends
'
address_internal_txs
'
?
AddressInternalTxsResponse
:
Q
extends
'
address_internal_txs
'
?
AddressInternalTxsResponse
:
Q
extends
'
address_token_transfers
'
?
AddressTokenTransferResponse
:
Q
extends
'
address_token_transfers
'
?
AddressTokenTransferResponse
:
...
...
stubs/address.ts
View file @
064c44d6
import
type
{
Address
,
AddressCoinBalanceHistoryItem
,
AddressCounters
,
AddressTokenBalance
}
from
'
types/api/address
'
;
import
type
{
Address
,
AddressCoinBalanceHistoryItem
,
AddressCounters
,
AddressT
absCounters
,
AddressT
okenBalance
}
from
'
types/api/address
'
;
import
type
{
AddressesItem
}
from
'
types/api/addresses
'
;
import
type
{
AddressesItem
}
from
'
types/api/addresses
'
;
import
{
ADDRESS_HASH
}
from
'
./addressParams
'
;
import
{
ADDRESS_HASH
}
from
'
./addressParams
'
;
...
@@ -42,6 +42,17 @@ export const ADDRESS_COUNTERS: AddressCounters = {
...
@@ -42,6 +42,17 @@ export const ADDRESS_COUNTERS: AddressCounters = {
validations_count
:
'
0
'
,
validations_count
:
'
0
'
,
};
};
export
const
ADDRESS_TABS_COUNTERS
:
AddressTabsCounters
=
{
coin_balances_count
:
10
,
internal_txs_count
:
10
,
logs_count
:
10
,
token_balances_count
:
10
,
token_transfers_count
:
10
,
transactions_count
:
10
,
validations_count
:
10
,
withdrawals_count
:
10
,
};
export
const
TOP_ADDRESS
:
AddressesItem
=
{
export
const
TOP_ADDRESS
:
AddressesItem
=
{
coin_balance
:
'
11886682377162664596540805
'
,
coin_balance
:
'
11886682377162664596540805
'
,
tx_count
:
'
1835
'
,
tx_count
:
'
1835
'
,
...
...
types/api/address.ts
View file @
064c44d6
...
@@ -12,6 +12,7 @@ export interface Address extends UserTags {
...
@@ -12,6 +12,7 @@ export interface Address extends UserTags {
creator_address_hash
:
string
|
null
;
creator_address_hash
:
string
|
null
;
creation_tx_hash
:
string
|
null
;
creation_tx_hash
:
string
|
null
;
exchange_rate
:
string
|
null
;
exchange_rate
:
string
|
null
;
// TODO: if we are happy with tabs-counters method, should we delete has_something fields?
has_beacon_chain_withdrawals
?:
boolean
;
has_beacon_chain_withdrawals
?:
boolean
;
has_custom_methods_read
:
boolean
;
has_custom_methods_read
:
boolean
;
has_custom_methods_write
:
boolean
;
has_custom_methods_write
:
boolean
;
...
@@ -149,3 +150,14 @@ export type AddressWithdrawalsItem = {
...
@@ -149,3 +150,14 @@ export type AddressWithdrawalsItem = {
timestamp
:
string
;
timestamp
:
string
;
validator_index
:
number
;
validator_index
:
number
;
}
}
export
type
AddressTabsCounters
=
{
coin_balances_count
:
number
;
internal_txs_count
:
number
;
logs_count
:
number
;
token_balances_count
:
number
;
token_transfers_count
:
number
;
transactions_count
:
number
;
validations_count
:
number
;
withdrawals_count
:
number
;
}
ui/pages/Address.tsx
View file @
064c44d6
...
@@ -12,7 +12,7 @@ import { useAppContext } from 'lib/contexts/app';
...
@@ -12,7 +12,7 @@ import { useAppContext } from 'lib/contexts/app';
import
useContractTabs
from
'
lib/hooks/useContractTabs
'
;
import
useContractTabs
from
'
lib/hooks/useContractTabs
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
getQueryParamString
from
'
lib/router/getQueryParamString
'
;
import
getQueryParamString
from
'
lib/router/getQueryParamString
'
;
import
{
ADDRESS_INFO
}
from
'
stubs/address
'
;
import
{
ADDRESS_INFO
,
ADDRESS_TABS_COUNTERS
}
from
'
stubs/address
'
;
import
AddressBlocksValidated
from
'
ui/address/AddressBlocksValidated
'
;
import
AddressBlocksValidated
from
'
ui/address/AddressBlocksValidated
'
;
import
AddressCoinBalance
from
'
ui/address/AddressCoinBalance
'
;
import
AddressCoinBalance
from
'
ui/address/AddressCoinBalance
'
;
import
AddressContract
from
'
ui/address/AddressContract
'
;
import
AddressContract
from
'
ui/address/AddressContract
'
;
...
@@ -54,24 +54,71 @@ const AddressPageContent = () => {
...
@@ -54,24 +54,71 @@ const AddressPageContent = () => {
},
},
});
});
const
addressTabsCountersQuery
=
useApiQuery
(
'
address_tabs_counters
'
,
{
pathParams
:
{
hash
},
queryOptions
:
{
enabled
:
Boolean
(
hash
),
placeholderData
:
ADDRESS_TABS_COUNTERS
,
},
});
const
contractTabs
=
useContractTabs
(
addressQuery
.
data
);
const
contractTabs
=
useContractTabs
(
addressQuery
.
data
);
const
tabs
:
Array
<
RoutedTab
>
=
React
.
useMemo
(()
=>
{
const
tabs
:
Array
<
RoutedTab
>
=
React
.
useMemo
(()
=>
{
return
[
return
[
{
id
:
'
txs
'
,
title
:
'
Transactions
'
,
component
:
<
AddressTxs
scrollRef=
{
tabsScrollRef
}
/>
},
{
config
.
features
.
beaconChain
.
isEnabled
&&
addressQuery
.
data
?.
has_beacon_chain_withdrawals
?
id
:
'
txs
'
,
{
id
:
'
withdrawals
'
,
title
:
'
Withdrawals
'
,
component
:
<
AddressWithdrawals
scrollRef=
{
tabsScrollRef
}
/>
}
:
title
:
'
Transactions
'
,
undefined
,
count
:
addressTabsCountersQuery
.
data
?.
transactions_count
,
addressQuery
.
data
?.
has_token_transfers
?
component
:
<
AddressTxs
scrollRef=
{
tabsScrollRef
}
/>,
{
id
:
'
token_transfers
'
,
title
:
'
Token transfers
'
,
component
:
<
AddressTokenTransfers
scrollRef=
{
tabsScrollRef
}
/>
}
:
},
config
.
features
.
beaconChain
.
isEnabled
?
{
id
:
'
withdrawals
'
,
title
:
'
Withdrawals
'
,
count
:
addressTabsCountersQuery
.
data
?.
withdrawals_count
,
component
:
<
AddressWithdrawals
scrollRef=
{
tabsScrollRef
}
/>,
}
:
undefined
,
undefined
,
addressQuery
.
data
?.
has_tokens
?
{
id
:
'
tokens
'
,
title
:
'
Tokens
'
,
component
:
<
AddressTokens
/>,
subTabs
:
TOKEN_TABS
}
:
undefined
,
{
{
id
:
'
internal_txns
'
,
title
:
'
Internal txns
'
,
component
:
<
AddressInternalTxs
scrollRef=
{
tabsScrollRef
}
/>
},
id
:
'
token_transfers
'
,
{
id
:
'
coin_balance_history
'
,
title
:
'
Coin balance history
'
,
component
:
<
AddressCoinBalance
/>
},
title
:
'
Token transfers
'
,
addressQuery
.
data
?.
has_validated_blocks
?
count
:
addressTabsCountersQuery
.
data
?.
token_transfers_count
,
{
id
:
'
blocks_validated
'
,
title
:
'
Blocks validated
'
,
component
:
<
AddressBlocksValidated
scrollRef=
{
tabsScrollRef
}
/>
}
:
component
:
<
AddressTokenTransfers
scrollRef=
{
tabsScrollRef
}
/>,
},
{
id
:
'
tokens
'
,
title
:
'
Tokens
'
,
count
:
addressTabsCountersQuery
.
data
?.
token_balances_count
,
component
:
<
AddressTokens
/>,
subTabs
:
TOKEN_TABS
,
},
{
id
:
'
internal_txns
'
,
title
:
'
Internal txns
'
,
count
:
addressTabsCountersQuery
.
data
?.
internal_txs_count
,
component
:
<
AddressInternalTxs
scrollRef=
{
tabsScrollRef
}
/>,
},
{
id
:
'
coin_balance_history
'
,
title
:
'
Coin balance history
'
,
count
:
addressTabsCountersQuery
.
data
?.
coin_balances_count
,
component
:
<
AddressCoinBalance
/>,
},
config
.
chain
.
verificationType
===
'
validation
'
?
{
id
:
'
blocks_validated
'
,
title
:
'
Blocks validated
'
,
count
:
addressTabsCountersQuery
.
data
?.
validations_count
,
component
:
<
AddressBlocksValidated
scrollRef=
{
tabsScrollRef
}
/>,
}
:
undefined
,
undefined
,
addressQuery
.
data
?.
has_logs
?
{
id
:
'
logs
'
,
title
:
'
Logs
'
,
component
:
<
AddressLogs
scrollRef=
{
tabsScrollRef
}
/>
}
:
undefined
,
{
id
:
'
logs
'
,
title
:
'
Logs
'
,
count
:
addressTabsCountersQuery
.
data
?.
logs_count
,
component
:
<
AddressLogs
scrollRef=
{
tabsScrollRef
}
/>,
},
addressQuery
.
data
?.
is_contract
?
{
addressQuery
.
data
?.
is_contract
?
{
id
:
'
contract
'
,
id
:
'
contract
'
,
title
:
()
=>
{
title
:
()
=>
{
...
@@ -90,7 +137,7 @@ const AddressPageContent = () => {
...
@@ -90,7 +137,7 @@ const AddressPageContent = () => {
subTabs
:
contractTabs
.
map
(
tab
=>
tab
.
id
),
subTabs
:
contractTabs
.
map
(
tab
=>
tab
.
id
),
}
:
undefined
,
}
:
undefined
,
].
filter
(
Boolean
);
].
filter
(
Boolean
);
},
[
addressQuery
.
data
,
contractTabs
]);
},
[
addressQuery
.
data
,
contractTabs
,
addressTabsCountersQuery
.
data
]);
const
tags
=
(
const
tags
=
(
<
EntityTags
<
EntityTags
...
@@ -134,7 +181,7 @@ const AddressPageContent = () => {
...
@@ -134,7 +181,7 @@ const AddressPageContent = () => {
<
AddressDetails
addressQuery=
{
addressQuery
}
scrollRef=
{
tabsScrollRef
}
/>
<
AddressDetails
addressQuery=
{
addressQuery
}
scrollRef=
{
tabsScrollRef
}
/>
{
/* should stay before tabs to scroll up with pagination */
}
{
/* should stay before tabs to scroll up with pagination */
}
<
Box
ref=
{
tabsScrollRef
}
></
Box
>
<
Box
ref=
{
tabsScrollRef
}
></
Box
>
{
addressQuery
.
isPlaceholderData
?
<
TabsSkeleton
tabs=
{
tabs
}
/>
:
content
}
{
(
addressQuery
.
isPlaceholderData
||
addressTabsCountersQuery
.
isPlaceholderData
)
?
<
TabsSkeleton
tabs=
{
tabs
}
/>
:
content
}
</>
</>
);
);
};
};
...
...
ui/shared/Tabs/TabCounter.tsx
0 → 100644
View file @
064c44d6
import
type
{
SystemStyleObject
}
from
'
@chakra-ui/react
'
;
import
{
Text
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
type
Props
=
{
count
?:
number
;
parentClassName
:
string
;
}
const
TasCounter
=
({
count
,
parentClassName
}:
Props
)
=>
{
const
zeroCountColor
=
useColorModeValue
(
'
blackAlpha.400
'
,
'
whiteAlpha.400
'
);
if
(
count
===
undefined
)
{
return
null
;
}
const
sx
:
SystemStyleObject
=
{};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore:
sx
[
`.
${
parentClassName
}
:hover &`
]
=
{
color
:
'
inherit
'
};
return
(
<
Text
color=
{
count
>
0
?
'
text_secondary
'
:
zeroCountColor
}
ml=
{
1
}
sx=
{
sx
}
transitionProperty=
"color"
transitionDuration=
"normal"
transitionTimingFunction=
"ease"
>
{
count
}
</
Text
>
);
};
export
default
TasCounter
;
ui/shared/Tabs/TabsMenu.tsx
View file @
064c44d6
...
@@ -12,8 +12,11 @@ import React from 'react';
...
@@ -12,8 +12,11 @@ import React from 'react';
import
type
{
MenuButton
,
TabItem
}
from
'
./types
'
;
import
type
{
MenuButton
,
TabItem
}
from
'
./types
'
;
import
TabCounter
from
'
./TabCounter
'
;
import
{
menuButton
}
from
'
./utils
'
;
import
{
menuButton
}
from
'
./utils
'
;
const
BUTTON_CLASSNAME
=
'
button-item
'
;
interface
Props
{
interface
Props
{
tabs
:
Array
<
TabItem
|
MenuButton
>
;
tabs
:
Array
<
TabItem
|
MenuButton
>
;
activeTab
?:
TabItem
;
activeTab
?:
TabItem
;
...
@@ -59,8 +62,10 @@ const TabsMenu = ({ tabs, tabsCut, isActive, styles, onItemClick, buttonRef, act
...
@@ -59,8 +62,10 @@ const TabsMenu = ({ tabs, tabsCut, isActive, styles, onItemClick, buttonRef, act
isActive=
{
activeTab
?
activeTab
.
id
===
tab
.
id
:
false
}
isActive=
{
activeTab
?
activeTab
.
id
===
tab
.
id
:
false
}
justifyContent=
"left"
justifyContent=
"left"
data
-
index=
{
index
}
data
-
index=
{
index
}
className=
{
BUTTON_CLASSNAME
}
>
>
{
typeof
tab
.
title
===
'
function
'
?
tab
.
title
()
:
tab
.
title
}
{
typeof
tab
.
title
===
'
function
'
?
tab
.
title
()
:
tab
.
title
}
<
TabCounter
count=
{
tab
.
count
}
parentClassName=
{
BUTTON_CLASSNAME
}
/>
</
Button
>
</
Button
>
))
}
))
}
</
PopoverBody
>
</
PopoverBody
>
...
...
ui/shared/Tabs/TabsWithScroll.pw.tsx
0 → 100644
View file @
064c44d6
import
{
test
,
expect
}
from
'
@playwright/experimental-ct-react
'
;
import
React
from
'
react
'
;
import
type
{
TabItem
}
from
'
./types
'
;
import
TestApp
from
'
playwright/TestApp
'
;
import
TabsWithScroll
from
'
./TabsWithScroll
'
;
test
(
'
with counters
'
,
async
({
mount
})
=>
{
const
tabs
:
Array
<
TabItem
>
=
[
{
id
:
'
tab1
'
,
title
:
'
First tab
'
,
count
:
11
,
component
:
null
,
},
{
id
:
'
tab2
'
,
title
:
'
Second tab
'
,
count
:
0
,
component
:
null
,
},
{
id
:
'
tab3
'
,
title
:
'
Third tab
'
,
count
:
1
,
component
:
null
,
},
];
const
component
=
await
mount
(
<
TestApp
>
<
TabsWithScroll
tabs=
{
tabs
}
/>
</
TestApp
>,
);
await
component
.
getByText
(
'
Third tab
'
).
hover
();
await
expect
(
component
).
toHaveScreenshot
();
});
ui/shared/Tabs/TabsWithScroll.tsx
View file @
064c44d6
...
@@ -19,9 +19,12 @@ import { useScrollDirection } from 'lib/contexts/scrollDirection';
...
@@ -19,9 +19,12 @@ import { useScrollDirection } from 'lib/contexts/scrollDirection';
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
useIsSticky
from
'
lib/hooks/useIsSticky
'
;
import
useIsSticky
from
'
lib/hooks/useIsSticky
'
;
import
TabCounter
from
'
./TabCounter
'
;
import
TabsMenu
from
'
./TabsMenu
'
;
import
TabsMenu
from
'
./TabsMenu
'
;
import
useAdaptiveTabs
from
'
./useAdaptiveTabs
'
;
import
useAdaptiveTabs
from
'
./useAdaptiveTabs
'
;
const
TAB_CLASSNAME
=
'
tab-item
'
;
const
hiddenItemStyles
:
StyleProps
=
{
const
hiddenItemStyles
:
StyleProps
=
{
position
:
'
absolute
'
,
position
:
'
absolute
'
,
top
:
'
-9999px
'
,
top
:
'
-9999px
'
,
...
@@ -167,8 +170,10 @@ const TabsWithScroll = ({
...
@@ -167,8 +170,10 @@ const TabsWithScroll = ({
{
...
(
index
<
tabsCut
?
{}
:
hiddenItemStyles
)
}
{
...
(
index
<
tabsCut
?
{}
:
hiddenItemStyles
)
}
scrollSnapAlign=
"start"
scrollSnapAlign=
"start"
flexShrink=
{
0
}
flexShrink=
{
0
}
className=
{
TAB_CLASSNAME
}
>
>
{
typeof
tab
.
title
===
'
function
'
?
tab
.
title
()
:
tab
.
title
}
{
typeof
tab
.
title
===
'
function
'
?
tab
.
title
()
:
tab
.
title
}
<
TabCounter
count=
{
tab
.
count
}
parentClassName=
{
TAB_CLASSNAME
}
/>
</
Tab
>
</
Tab
>
);
);
})
}
})
}
...
...
ui/shared/Tabs/__screenshots__/TabsWithScroll.pw.tsx_default_with-counters-1.png
0 → 100644
View file @
064c44d6
4.95 KB
ui/shared/Tabs/types.ts
View file @
064c44d6
...
@@ -3,6 +3,7 @@ import type React from 'react';
...
@@ -3,6 +3,7 @@ import type React from 'react';
export
interface
TabItem
{
export
interface
TabItem
{
id
:
string
;
id
:
string
;
title
:
string
|
(()
=>
React
.
ReactNode
);
title
:
string
|
(()
=>
React
.
ReactNode
);
count
?:
number
;
component
:
React
.
ReactNode
;
component
:
React
.
ReactNode
;
}
}
...
@@ -13,5 +14,6 @@ export type RoutedSubTab = Omit<TabItem, 'subTabs'>;
...
@@ -13,5 +14,6 @@ export type RoutedSubTab = Omit<TabItem, 'subTabs'>;
export
interface
MenuButton
{
export
interface
MenuButton
{
id
:
null
;
id
:
null
;
title
:
string
;
title
:
string
;
count
?:
never
;
component
:
null
;
component
:
null
;
}
}
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