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
1484bb36
Commit
1484bb36
authored
Dec 26, 2023
by
tom
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'main' of github.com:blockscout/frontend into tom2drum/issue-1414
parents
f8d8b7f8
95243dc7
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
221 additions
and
109 deletions
+221
-109
cleanup.yml
.github/workflows/cleanup.yml
+1
-1
tasks.json
.vscode/tasks.json
+1
-0
.env.sepolia
configs/envs/.env.sepolia
+60
-0
index.tsx
pages/apps/index.tsx
+12
-1
ContractCode.pw.tsx
ui/address/contract/ContractCode.pw.tsx
+25
-0
ContractCode.tsx
ui/address/contract/ContractCode.tsx
+3
-4
ERC20TokensTableItem.tsx
ui/address/tokens/ERC20TokensTableItem.tsx
+8
-2
Marketplace.tsx
ui/pages/Marketplace.tsx
+1
-25
AddressAddToWallet.tsx
ui/shared/address/AddressAddToWallet.tsx
+1
-1
LayoutApp.tsx
ui/shared/layout/LayoutApp.tsx
+2
-1
ProfileMenuDesktop.tsx
ui/snippets/profileMenu/ProfileMenuDesktop.tsx
+1
-1
SearchBar.tsx
ui/snippets/searchBar/SearchBar.tsx
+48
-45
SearchBarInput.tsx
ui/snippets/searchBar/SearchBarInput.tsx
+16
-16
WalletMenuDesktop.tsx
ui/snippets/walletMenu/WalletMenuDesktop.tsx
+3
-1
WalletMenuMobile.tsx
ui/snippets/walletMenu/WalletMenuMobile.tsx
+3
-1
WalletTooltip.tsx
ui/snippets/walletMenu/WalletTooltip.tsx
+23
-8
TokensTableItem.tsx
ui/tokens/TokensTableItem.tsx
+13
-2
No files found.
.github/workflows/cleanup.yml
View file @
1484bb36
...
@@ -29,5 +29,5 @@ jobs:
...
@@ -29,5 +29,5 @@ jobs:
cleanup_docker_image
:
cleanup_docker_image
:
uses
:
blockscout/blockscout-ci-cd/.github/workflows/cleanup_docker.yaml@master
uses
:
blockscout/blockscout-ci-cd/.github/workflows/cleanup_docker.yaml@master
with
:
with
:
dockerImage
:
prerelease
-$GITHUB_REF_NAME_SLUG
dockerImage
:
review
-$GITHUB_REF_NAME_SLUG
secrets
:
inherit
secrets
:
inherit
.vscode/tasks.json
View file @
1484bb36
...
@@ -318,6 +318,7 @@
...
@@ -318,6 +318,7 @@
"main.L2"
,
"main.L2"
,
"poa_core"
,
"poa_core"
,
"eth_goerli"
,
"eth_goerli"
,
"sepolia"
,
"eth"
,
"eth"
,
"rootstock"
,
"rootstock"
,
"polygon"
,
"polygon"
,
...
...
configs/envs/.env.sepolia
0 → 100644
View file @
1484bb36
# Set of ENVs for Sepolia testnet network explorer
# https://eth-sepolia.blockscout.com/
# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=Sepolia
NEXT_PUBLIC_NETWORK_SHORT_NAME=Sepolia
NEXT_PUBLIC_NETWORK_ID=11155111
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://eth-sepolia.public.blastapi.io
NEXT_PUBLIC_IS_TESTNET=true
# api configuration
NEXT_PUBLIC_API_HOST=eth-sepolia.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgba(51, 53, 67, 1)'
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='rgba(165, 252, 122, 1)'
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png
NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png
NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}]
## footer
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/sepolia.json
##views
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'LooksRare','collection_url':'https://sepolia.looksrare.org/collections/{hash}','instance_url':'https://sepolia.looksrare.org/collections/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/looks-rare.png'}]
## misc
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Etherscan','baseUrl':'https://sepolia.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}},{'title':'Tenderly','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/sepolia'}}]
# app features
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xbf69c7abc4fee283b59a9633dadfdaedde5c5ee0fba3e80a08b5b8a3acbd4363
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrqUAcjgGJ4jU88C
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask']
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_HAS_BEACON_CHAIN=true
#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png
pages/apps/index.tsx
View file @
1484bb36
...
@@ -4,15 +4,26 @@ import React from 'react';
...
@@ -4,15 +4,26 @@ import React from 'react';
import
PageNextJs
from
'
nextjs/PageNextJs
'
;
import
PageNextJs
from
'
nextjs/PageNextJs
'
;
import
config
from
'
configs/app
'
;
import
LinkExternal
from
'
ui/shared/LinkExternal
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
const
feature
=
config
.
features
.
marketplace
;
const
Marketplace
=
dynamic
(()
=>
import
(
'
ui/pages/Marketplace
'
),
{
ssr
:
false
});
const
Marketplace
=
dynamic
(()
=>
import
(
'
ui/pages/Marketplace
'
),
{
ssr
:
false
});
const
Page
:
NextPage
=
()
=>
{
const
Page
:
NextPage
=
()
=>
{
return
(
return
(
<
PageNextJs
pathname=
"/apps"
>
<
PageNextJs
pathname=
"/apps"
>
<>
<>
<
PageTitle
title=
"DAppscout"
/>
<
PageTitle
title=
"DAppscout"
contentAfter=
{
feature
.
isEnabled
&&
(
<
LinkExternal
href=
{
feature
.
submitFormUrl
}
variant=
"subtle"
fontSize=
"sm"
lineHeight=
{
5
}
ml=
"auto"
>
Submit app
</
LinkExternal
>
)
}
/>
<
Marketplace
/>
<
Marketplace
/>
</>
</>
</
PageNextJs
>
</
PageNextJs
>
...
...
ui/address/contract/ContractCode.pw.tsx
View file @
1484bb36
...
@@ -78,6 +78,31 @@ test('verified with changed byte code socket', async({ mount, page, createSocket
...
@@ -78,6 +78,31 @@ test('verified with changed byte code socket', async({ mount, page, createSocket
await
expect
(
component
).
toHaveScreenshot
();
await
expect
(
component
).
toHaveScreenshot
();
});
});
test
(
'
verified via lookup in eth_bytecode_db
'
,
async
({
mount
,
page
,
createSocket
})
=>
{
await
page
.
route
(
CONTRACT_API_URL
,
(
route
)
=>
route
.
fulfill
({
status
:
200
,
body
:
JSON
.
stringify
(
contractMock
.
nonVerified
),
}));
await
page
.
route
(
'
https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/**
'
,
(
route
)
=>
route
.
abort
());
await
mount
(
<
TestApp
withSocket
>
<
ContractCode
addressHash=
{
addressHash
}
/>
</
TestApp
>,
{
hooksConfig
},
);
const
socket
=
await
createSocket
();
const
channel
=
await
socketServer
.
joinChannel
(
socket
,
'
addresses:
'
+
addressHash
.
toLowerCase
());
await
page
.
waitForResponse
(
CONTRACT_API_URL
);
socketServer
.
sendMessage
(
socket
,
channel
,
'
smart_contract_was_verified
'
,
{});
const
request
=
await
page
.
waitForRequest
(
CONTRACT_API_URL
);
expect
(
request
).
toBeTruthy
();
});
test
(
'
verified with multiple sources
'
,
async
({
mount
,
page
})
=>
{
test
(
'
verified with multiple sources
'
,
async
({
mount
,
page
})
=>
{
await
page
.
route
(
CONTRACT_API_URL
,
(
route
)
=>
route
.
fulfill
({
await
page
.
route
(
CONTRACT_API_URL
,
(
route
)
=>
route
.
fulfill
({
status
:
200
,
status
:
200
,
...
...
ui/address/contract/ContractCode.tsx
View file @
1484bb36
...
@@ -38,7 +38,6 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
...
@@ -38,7 +38,6 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
const
[
isChangedBytecodeSocket
,
setIsChangedBytecodeSocket
]
=
React
.
useState
<
boolean
>
();
const
[
isChangedBytecodeSocket
,
setIsChangedBytecodeSocket
]
=
React
.
useState
<
boolean
>
();
const
queryClient
=
useQueryClient
();
const
queryClient
=
useQueryClient
();
const
refetchQueries
=
queryClient
.
refetchQueries
;
const
addressInfo
=
queryClient
.
getQueryData
<
AddressInfo
>
(
getResourceKey
(
'
address
'
,
{
pathParams
:
{
hash
:
addressHash
}
}));
const
addressInfo
=
queryClient
.
getQueryData
<
AddressInfo
>
(
getResourceKey
(
'
address
'
,
{
pathParams
:
{
hash
:
addressHash
}
}));
const
{
data
,
isPlaceholderData
,
isError
}
=
useApiQuery
(
'
contract
'
,
{
const
{
data
,
isPlaceholderData
,
isError
}
=
useApiQuery
(
'
contract
'
,
{
...
@@ -55,13 +54,13 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
...
@@ -55,13 +54,13 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
},
[
]);
},
[
]);
const
handleContractWasVerifiedMessage
:
SocketMessage
.
SmartContractWasVerified
[
'
handler
'
]
=
React
.
useCallback
(()
=>
{
const
handleContractWasVerifiedMessage
:
SocketMessage
.
SmartContractWasVerified
[
'
handler
'
]
=
React
.
useCallback
(()
=>
{
refetchQueries
({
queryClient
.
refetchQueries
({
queryKey
:
getResourceKey
(
'
address
'
,
{
pathParams
:
{
hash
:
addressHash
}
}),
queryKey
:
getResourceKey
(
'
address
'
,
{
pathParams
:
{
hash
:
addressHash
}
}),
});
});
refetchQueries
({
queryClient
.
refetchQueries
({
queryKey
:
getResourceKey
(
'
contract
'
,
{
pathParams
:
{
hash
:
addressHash
}
}),
queryKey
:
getResourceKey
(
'
contract
'
,
{
pathParams
:
{
hash
:
addressHash
}
}),
});
});
},
[
addressHash
,
refetchQueries
]);
},
[
addressHash
,
queryClient
]);
const
enableQuery
=
React
.
useCallback
(()
=>
setIsQueryEnabled
(
true
),
[]);
const
enableQuery
=
React
.
useCallback
(()
=>
setIsQueryEnabled
(
true
),
[]);
...
...
ui/address/tokens/ERC20TokensTableItem.tsx
View file @
1484bb36
...
@@ -22,7 +22,13 @@ const ERC20TokensTableItem = ({
...
@@ -22,7 +22,13 @@ const ERC20TokensTableItem = ({
}
=
getCurrencyValue
({
value
:
value
,
exchangeRate
:
token
.
exchange_rate
,
decimals
:
token
.
decimals
,
accuracy
:
8
,
accuracyUsd
:
2
});
}
=
getCurrencyValue
({
value
:
value
,
exchangeRate
:
token
.
exchange_rate
,
decimals
:
token
.
decimals
,
accuracy
:
8
,
accuracyUsd
:
2
});
return
(
return
(
<
Tr
>
<
Tr
sx=
{
{
'
&:hover [aria-label="Add token to wallet"]
'
:
{
opacity
:
1
,
},
}
}
>
<
Td
verticalAlign=
"middle"
>
<
Td
verticalAlign=
"middle"
>
<
TokenEntity
<
TokenEntity
token=
{
token
}
token=
{
token
}
...
@@ -39,7 +45,7 @@ const ERC20TokensTableItem = ({
...
@@ -39,7 +45,7 @@ const ERC20TokensTableItem = ({
isLoading=
{
isLoading
}
isLoading=
{
isLoading
}
noIcon
noIcon
/>
/>
<
AddressAddToWallet
token=
{
token
}
ml=
{
4
}
isLoading=
{
isLoading
}
/>
<
AddressAddToWallet
token=
{
token
}
ml=
{
4
}
isLoading=
{
isLoading
}
opacity=
"0"
/>
</
Flex
>
</
Flex
>
</
Td
>
</
Td
>
<
Td
isNumeric
verticalAlign=
"middle"
>
<
Td
isNumeric
verticalAlign=
"middle"
>
...
...
ui/pages/Marketplace.tsx
View file @
1484bb36
import
{
Box
,
Link
,
Skeleton
}
from
'
@chakra-ui/react
'
;
import
{
Box
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
config
from
'
configs/app
'
;
import
config
from
'
configs/app
'
;
...
@@ -7,7 +7,6 @@ import MarketplaceCategoriesMenu from 'ui/marketplace/MarketplaceCategoriesMenu'
...
@@ -7,7 +7,6 @@ import MarketplaceCategoriesMenu from 'ui/marketplace/MarketplaceCategoriesMenu'
import
MarketplaceDisclaimerModal
from
'
ui/marketplace/MarketplaceDisclaimerModal
'
;
import
MarketplaceDisclaimerModal
from
'
ui/marketplace/MarketplaceDisclaimerModal
'
;
import
MarketplaceList
from
'
ui/marketplace/MarketplaceList
'
;
import
MarketplaceList
from
'
ui/marketplace/MarketplaceList
'
;
import
FilterInput
from
'
ui/shared/filters/FilterInput
'
;
import
FilterInput
from
'
ui/shared/filters/FilterInput
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
useMarketplace
from
'
../marketplace/useMarketplace
'
;
import
useMarketplace
from
'
../marketplace/useMarketplace
'
;
const
feature
=
config
.
features
.
marketplace
;
const
feature
=
config
.
features
.
marketplace
;
...
@@ -91,29 +90,6 @@ const Marketplace = () => {
...
@@ -91,29 +90,6 @@ const Marketplace = () => {
appId=
{
selectedApp
.
id
}
appId=
{
selectedApp
.
id
}
/>
/>
)
}
)
}
<
Skeleton
isLoaded=
{
!
isPlaceholderData
}
marginTop=
{
{
base
:
8
,
sm
:
16
}
}
display=
"inline-block"
>
<
Link
fontWeight=
"bold"
display=
"inline-flex"
alignItems=
"baseline"
href=
{
feature
.
submitFormUrl
}
isExternal
>
<
IconSvg
name=
"plus"
w=
{
3
}
h=
{
3
}
mr=
{
2
}
/>
Submit an app
</
Link
>
</
Skeleton
>
</>
</>
);
);
};
};
...
...
ui/shared/address/AddressAddToWallet.tsx
View file @
1484bb36
...
@@ -107,7 +107,7 @@ const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', ico
...
@@ -107,7 +107,7 @@ const AddressAddToWallet = ({ className, token, isLoading, variant = 'icon', ico
return
(
return
(
<
Tooltip
label=
{
`Add token to ${ WALLETS_INFO[wallet].name }`
}
>
<
Tooltip
label=
{
`Add token to ${ WALLETS_INFO[wallet].name }`
}
>
<
Box
className=
{
className
}
display=
"inline-flex"
cursor=
"pointer"
onClick=
{
handleClick
}
flexShrink=
{
0
}
>
<
Box
className=
{
className
}
display=
"inline-flex"
cursor=
"pointer"
onClick=
{
handleClick
}
flexShrink=
{
0
}
aria
-
label=
"Add token to wallet"
>
<
IconSvg
name=
{
WALLETS_INFO
[
wallet
].
icon
}
boxSize=
{
iconSize
}
/>
<
IconSvg
name=
{
WALLETS_INFO
[
wallet
].
icon
}
boxSize=
{
iconSize
}
/>
</
Box
>
</
Box
>
</
Tooltip
>
</
Tooltip
>
...
...
ui/shared/layout/LayoutApp.tsx
View file @
1484bb36
...
@@ -12,10 +12,11 @@ import * as Layout from './components';
...
@@ -12,10 +12,11 @@ import * as Layout from './components';
const
LayoutDefault
=
({
children
}:
Props
)
=>
{
const
LayoutDefault
=
({
children
}:
Props
)
=>
{
return
(
return
(
<
Layout
.
Container
>
<
Layout
.
Container
>
<
Layout
.
TopRow
/>
<
HeaderMobile
/>
<
HeaderMobile
/>
<
Layout
.
MainArea
>
<
Layout
.
MainArea
>
<
Layout
.
MainColumn
<
Layout
.
MainColumn
paddingTop=
{
{
base
:
'
138px
'
,
lg
:
6
}
}
paddingTop=
{
{
base
:
16
,
lg
:
6
}
}
paddingX=
{
{
base
:
4
,
lg
:
6
}
}
paddingX=
{
{
base
:
4
,
lg
:
6
}
}
>
>
<
HeaderAlert
/>
<
HeaderAlert
/>
...
...
ui/snippets/profileMenu/ProfileMenuDesktop.tsx
View file @
1484bb36
...
@@ -76,7 +76,7 @@ const ProfileMenuDesktop = ({ isHomePage }: Props) => {
...
@@ -76,7 +76,7 @@ const ProfileMenuDesktop = ({ isHomePage }: Props) => {
textAlign=
"center"
textAlign=
"center"
padding=
{
2
}
padding=
{
2
}
isDisabled=
{
hasMenu
}
isDisabled=
{
hasMenu
}
openDelay=
{
3
00
}
openDelay=
{
5
00
}
>
>
<
Box
>
<
Box
>
<
PopoverTrigger
>
<
PopoverTrigger
>
...
...
ui/snippets/searchBar/SearchBar.tsx
View file @
1484bb36
import
{
Box
,
Po
pover
,
PopoverTrigger
,
PopoverContent
,
PopoverBody
,
useDisclosure
,
PopoverFooter
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Po
rtal
,
Popover
,
PopoverTrigger
,
PopoverContent
,
PopoverBody
,
useDisclosure
,
PopoverFooter
,
useOutsideClick
}
from
'
@chakra-ui/react
'
;
import
_debounce
from
'
lodash/debounce
'
;
import
_debounce
from
'
lodash/debounce
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
type
{
FormEvent
,
FocusEvent
}
from
'
react
'
;
import
type
{
FormEvent
}
from
'
react
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
{
Element
}
from
'
react-scroll
'
;
import
{
Element
}
from
'
react-scroll
'
;
...
@@ -59,13 +59,15 @@ const SearchBar = ({ isHomepage }: Props) => {
...
@@ -59,13 +59,15 @@ const SearchBar = ({ isHomepage }: Props) => {
inputRef
.
current
?.
querySelector
(
'
input
'
)?.
blur
();
inputRef
.
current
?.
querySelector
(
'
input
'
)?.
blur
();
},
[
onClose
]);
},
[
onClose
]);
const
handle
Blur
=
React
.
useCallback
((
event
:
FocusEvent
<
HTMLFormElement
>
)
=>
{
const
handle
OutsideClick
=
React
.
useCallback
((
event
:
Event
)
=>
{
const
isFocusIn
Menu
=
menuRef
.
current
?.
contains
(
event
.
relatedTarget
);
const
isFocusIn
Input
=
inputRef
.
current
?.
contains
(
event
.
target
as
Node
);
const
isFocusInInput
=
inputRef
.
current
?.
contains
(
event
.
relatedTarget
);
if
(
!
isFocusIn
Menu
&&
!
isFocusIn
Input
)
{
if
(
!
isFocusInInput
)
{
onClos
e
();
handelHid
e
();
}
}
},
[
onClose
]);
},
[
handelHide
]);
useOutsideClick
({
ref
:
menuRef
,
handler
:
handleOutsideClick
});
const
handleClear
=
React
.
useCallback
(()
=>
{
const
handleClear
=
React
.
useCallback
(()
=>
{
handleSearchTermChange
(
''
);
handleSearchTermChange
(
''
);
...
@@ -118,53 +120,54 @@ const SearchBar = ({ isHomepage }: Props) => {
...
@@ -118,53 +120,54 @@ const SearchBar = ({ isHomepage }: Props) => {
onChange=
{
handleSearchTermChange
}
onChange=
{
handleSearchTermChange
}
onSubmit=
{
handleSubmit
}
onSubmit=
{
handleSubmit
}
onFocus=
{
handleFocus
}
onFocus=
{
handleFocus
}
onBlur=
{
handleBlur
}
onHide=
{
handelHide
}
onHide=
{
handelHide
}
onClear=
{
handleClear
}
onClear=
{
handleClear
}
isHomepage=
{
isHomepage
}
isHomepage=
{
isHomepage
}
value=
{
searchTerm
}
value=
{
searchTerm
}
/>
/>
</
PopoverTrigger
>
</
PopoverTrigger
>
<
PopoverContent
<
Portal
>
w=
{
`${ menuWidth.current }px`
}
<
PopoverContent
ref=
{
menuRef
}
w=
{
`${ menuWidth.current }px`
}
>
ref=
{
menuRef
}
<
PopoverBody
p=
{
0
}
color=
"chakra-body-text"
>
>
<
Box
<
PopoverBody
maxH=
"50vh"
p=
{
0
}
overflowY=
"auto"
color=
"chakra-body-text"
id=
{
SCROLL_CONTAINER_ID
}
ref=
{
scrollRef
}
as=
{
Element
}
px=
{
4
}
>
>
{
searchTerm
.
trim
().
length
===
0
&&
recentSearchKeywords
.
length
>
0
&&
(
<
Box
<
SearchBarRecentKeywords
onClick=
{
handleSearchTermChange
}
onClear=
{
onClose
}
/>
maxH=
"50vh"
)
}
overflowY=
"auto"
{
searchTerm
.
trim
().
length
>
0
&&
(
id=
{
SCROLL_CONTAINER_ID
}
<
SearchBarSuggest
ref=
{
scrollRef
}
query=
{
query
}
as=
{
Element
}
searchTerm=
{
debouncedSearchTerm
}
px=
{
4
}
onItemClick=
{
handleItemClick
}
containerId=
{
SCROLL_CONTAINER_ID
}
/>
)
}
</
Box
>
</
PopoverBody
>
{
searchTerm
.
trim
().
length
>
0
&&
query
.
data
&&
query
.
data
.
length
>=
50
&&
(
<
PopoverFooter
>
<
LinkInternal
href=
{
route
({
pathname
:
'
/search-results
'
,
query
:
{
q
:
searchTerm
}
})
}
fontSize=
"sm"
>
>
{
searchTerm
.
trim
().
length
===
0
&&
recentSearchKeywords
.
length
>
0
&&
(
<
SearchBarRecentKeywords
onClick=
{
handleSearchTermChange
}
onClear=
{
onClose
}
/>
)
}
{
searchTerm
.
trim
().
length
>
0
&&
(
<
SearchBarSuggest
query=
{
query
}
searchTerm=
{
debouncedSearchTerm
}
onItemClick=
{
handleItemClick
}
containerId=
{
SCROLL_CONTAINER_ID
}
/>
)
}
</
Box
>
</
PopoverBody
>
{
searchTerm
.
trim
().
length
>
0
&&
query
.
data
&&
query
.
data
.
length
>=
50
&&
(
<
PopoverFooter
>
<
LinkInternal
href=
{
route
({
pathname
:
'
/search-results
'
,
query
:
{
q
:
searchTerm
}
})
}
fontSize=
"sm"
>
View all results
View all results
</
LinkInternal
>
</
LinkInternal
>
</
PopoverFooter
>
</
PopoverFooter
>
)
}
)
}
</
PopoverContent
>
</
PopoverContent
>
</
Portal
>
</
Popover
>
</
Popover
>
);
);
};
};
...
...
ui/snippets/searchBar/SearchBarInput.tsx
View file @
1484bb36
...
@@ -20,25 +20,33 @@ interface Props {
...
@@ -20,25 +20,33 @@ interface Props {
}
}
const
SearchBarInput
=
({
onChange
,
onSubmit
,
isHomepage
,
onFocus
,
onBlur
,
onHide
,
onClear
,
value
}:
Props
,
ref
:
React
.
ForwardedRef
<
HTMLFormElement
>
)
=>
{
const
SearchBarInput
=
({
onChange
,
onSubmit
,
isHomepage
,
onFocus
,
onBlur
,
onHide
,
onClear
,
value
}:
Props
,
ref
:
React
.
ForwardedRef
<
HTMLFormElement
>
)
=>
{
const
innerRef
=
React
.
useRef
<
HTMLFormElement
>
(
null
);
React
.
useImperativeHandle
(
ref
,
()
=>
innerRef
.
current
as
HTMLFormElement
,
[]);
const
[
isSticky
,
setIsSticky
]
=
React
.
useState
(
false
);
const
[
isSticky
,
setIsSticky
]
=
React
.
useState
(
false
);
const
scrollDirection
=
useScrollDirection
();
const
scrollDirection
=
useScrollDirection
();
const
isMobile
=
useIsMobile
();
const
isMobile
=
useIsMobile
();
const
handleScroll
=
React
.
useCallback
(()
=>
{
const
handleScroll
=
React
.
useCallback
(()
=>
{
const
TOP_BAR_HEIGHT
=
36
;
const
TOP_BAR_HEIGHT
=
36
;
if
(
window
.
pageYOffset
>=
TOP_BAR_HEIGHT
)
{
if
(
!
isHomepage
)
{
setIsSticky
(
true
);
if
(
window
.
scrollY
>=
TOP_BAR_HEIGHT
)
{
}
else
{
setIsSticky
(
true
);
setIsSticky
(
false
);
}
else
{
setIsSticky
(
false
);
}
}
}
},
[
]);
const
clientRect
=
isMobile
&&
innerRef
?.
current
?.
getBoundingClientRect
();
if
(
clientRect
&&
clientRect
.
y
<
TOP_BAR_HEIGHT
)
{
onHide
?.();
}
},
[
isMobile
,
onHide
,
isHomepage
]);
const
handleChange
=
React
.
useCallback
((
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
handleChange
=
React
.
useCallback
((
event
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
onChange
(
event
.
target
.
value
);
onChange
(
event
.
target
.
value
);
},
[
onChange
]);
},
[
onChange
]);
React
.
useEffect
(()
=>
{
React
.
useEffect
(()
=>
{
if
(
!
isMobile
||
isHomepage
)
{
if
(
!
isMobile
)
{
return
;
return
;
}
}
const
throttledHandleScroll
=
throttle
(
handleScroll
,
300
);
const
throttledHandleScroll
=
throttle
(
handleScroll
,
300
);
...
@@ -48,22 +56,14 @@ const SearchBarInput = ({ onChange, onSubmit, isHomepage, onFocus, onBlur, onHid
...
@@ -48,22 +56,14 @@ const SearchBarInput = ({ onChange, onSubmit, isHomepage, onFocus, onBlur, onHid
return
()
=>
{
return
()
=>
{
window
.
removeEventListener
(
'
scroll
'
,
throttledHandleScroll
);
window
.
removeEventListener
(
'
scroll
'
,
throttledHandleScroll
);
};
};
// replicate componentDidMount
},
[
isMobile
,
handleScroll
]);
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
isMobile
]);
const
bgColor
=
useColorModeValue
(
'
white
'
,
'
black
'
);
const
bgColor
=
useColorModeValue
(
'
white
'
,
'
black
'
);
const
transformMobile
=
scrollDirection
!==
'
down
'
?
'
translateY(0)
'
:
'
translateY(-100%)
'
;
const
transformMobile
=
scrollDirection
!==
'
down
'
?
'
translateY(0)
'
:
'
translateY(-100%)
'
;
React
.
useEffect
(()
=>
{
if
(
isMobile
&&
scrollDirection
===
'
down
'
)
{
onHide
?.();
}
},
[
scrollDirection
,
onHide
,
isMobile
]);
return
(
return
(
<
chakra
.
form
<
chakra
.
form
ref=
{
r
ef
}
ref=
{
innerR
ef
}
noValidate
noValidate
onSubmit=
{
onSubmit
}
onSubmit=
{
onSubmit
}
onBlur=
{
onBlur
}
onBlur=
{
onBlur
}
...
...
ui/snippets/walletMenu/WalletMenuDesktop.tsx
View file @
1484bb36
...
@@ -2,6 +2,7 @@ import type { ButtonProps } from '@chakra-ui/react';
...
@@ -2,6 +2,7 @@ import type { ButtonProps } from '@chakra-ui/react';
import
{
Popover
,
PopoverContent
,
PopoverBody
,
PopoverTrigger
,
Button
,
Box
,
useBoolean
}
from
'
@chakra-ui/react
'
;
import
{
Popover
,
PopoverContent
,
PopoverBody
,
PopoverTrigger
,
Button
,
Box
,
useBoolean
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
AddressIdenticon
from
'
ui/shared/entities/address/AddressIdenticon
'
;
import
AddressIdenticon
from
'
ui/shared/entities/address/AddressIdenticon
'
;
import
HashStringShorten
from
'
ui/shared/HashStringShorten
'
;
import
HashStringShorten
from
'
ui/shared/HashStringShorten
'
;
import
useWallet
from
'
ui/snippets/walletMenu/useWallet
'
;
import
useWallet
from
'
ui/snippets/walletMenu/useWallet
'
;
...
@@ -18,6 +19,7 @@ const WalletMenuDesktop = ({ isHomePage }: Props) => {
...
@@ -18,6 +19,7 @@ const WalletMenuDesktop = ({ isHomePage }: Props) => {
const
{
isWalletConnected
,
address
,
connect
,
disconnect
,
isModalOpening
,
isModalOpen
}
=
useWallet
();
const
{
isWalletConnected
,
address
,
connect
,
disconnect
,
isModalOpening
,
isModalOpen
}
=
useWallet
();
const
{
themedBackground
,
themedBorderColor
,
themedColor
}
=
useMenuButtonColors
();
const
{
themedBackground
,
themedBorderColor
,
themedColor
}
=
useMenuButtonColors
();
const
[
isPopoverOpen
,
setIsPopoverOpen
]
=
useBoolean
(
false
);
const
[
isPopoverOpen
,
setIsPopoverOpen
]
=
useBoolean
(
false
);
const
isMobile
=
useIsMobile
();
const
variant
=
React
.
useMemo
(()
=>
{
const
variant
=
React
.
useMemo
(()
=>
{
if
(
isWalletConnected
)
{
if
(
isWalletConnected
)
{
...
@@ -55,7 +57,7 @@ const WalletMenuDesktop = ({ isHomePage }: Props) => {
...
@@ -55,7 +57,7 @@ const WalletMenuDesktop = ({ isHomePage }: Props) => {
isOpen=
{
isPopoverOpen
}
isOpen=
{
isPopoverOpen
}
onClose=
{
setIsPopoverOpen
.
off
}
onClose=
{
setIsPopoverOpen
.
off
}
>
>
<
WalletTooltip
isDisabled=
{
isWalletConnected
}
>
<
WalletTooltip
isDisabled=
{
isWalletConnected
||
isMobile
===
undefined
||
isMobile
}
>
<
Box
ml=
{
2
}
>
<
Box
ml=
{
2
}
>
<
PopoverTrigger
>
<
PopoverTrigger
>
<
Button
<
Button
...
...
ui/snippets/walletMenu/WalletMenuMobile.tsx
View file @
1484bb36
import
{
Drawer
,
DrawerOverlay
,
DrawerContent
,
DrawerBody
,
useDisclosure
,
IconButton
}
from
'
@chakra-ui/react
'
;
import
{
Drawer
,
DrawerOverlay
,
DrawerContent
,
DrawerBody
,
useDisclosure
,
IconButton
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
AddressIdenticon
from
'
ui/shared/entities/address/AddressIdenticon
'
;
import
AddressIdenticon
from
'
ui/shared/entities/address/AddressIdenticon
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
useWallet
from
'
ui/snippets/walletMenu/useWallet
'
;
import
useWallet
from
'
ui/snippets/walletMenu/useWallet
'
;
...
@@ -13,10 +14,11 @@ const WalletMenuMobile = () => {
...
@@ -13,10 +14,11 @@ const WalletMenuMobile = () => {
const
{
isOpen
,
onOpen
,
onClose
}
=
useDisclosure
();
const
{
isOpen
,
onOpen
,
onClose
}
=
useDisclosure
();
const
{
isWalletConnected
,
address
,
connect
,
disconnect
,
isModalOpening
,
isModalOpen
}
=
useWallet
();
const
{
isWalletConnected
,
address
,
connect
,
disconnect
,
isModalOpening
,
isModalOpen
}
=
useWallet
();
const
{
themedBackground
,
themedBorderColor
,
themedColor
}
=
useMenuButtonColors
();
const
{
themedBackground
,
themedBorderColor
,
themedColor
}
=
useMenuButtonColors
();
const
isMobile
=
useIsMobile
();
return
(
return
(
<>
<>
<
WalletTooltip
isDisabled=
{
isWalletConnected
}
isMobile
>
<
WalletTooltip
isDisabled=
{
isWalletConnected
||
isMobile
===
undefined
||
!
isMobile
}
isMobile
>
<
IconButton
<
IconButton
aria
-
label=
"wallet menu"
aria
-
label=
"wallet menu"
icon=
{
isWalletConnected
?
icon=
{
isWalletConnected
?
...
...
ui/snippets/walletMenu/WalletTooltip.tsx
View file @
1484bb36
import
{
Tooltip
,
useBoolean
}
from
'
@chakra-ui/react
'
;
import
{
Tooltip
,
useBoolean
,
useOutsideClick
}
from
'
@chakra-ui/react
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
type
Props
=
{
type
Props
=
{
...
@@ -8,27 +9,41 @@ type Props = {
...
@@ -8,27 +9,41 @@ type Props = {
};
};
const
WalletTooltip
=
({
children
,
isDisabled
,
isMobile
}:
Props
)
=>
{
const
WalletTooltip
=
({
children
,
isDisabled
,
isMobile
}:
Props
)
=>
{
const
router
=
useRouter
();
const
[
isTooltipShown
,
setIsTooltipShown
]
=
useBoolean
(
false
);
const
[
isTooltipShown
,
setIsTooltipShown
]
=
useBoolean
(
false
);
const
ref
=
React
.
useRef
(
null
);
useOutsideClick
({
ref
,
handler
:
setIsTooltipShown
.
off
});
const
{
defaultLabel
,
label
,
localStorageKey
}
=
React
.
useMemo
(()
=>
{
const
isAppPage
=
router
.
pathname
===
'
/apps/[id]
'
;
const
defaultLabel
=
<
span
>
Your wallet is used to interact with
<
br
/>
apps and contracts in the explorer
</
span
>;
const
label
=
isAppPage
?
<
span
>
Connect once to use your wallet with
<
br
/>
all apps in the DAppscout marketplace!
</
span
>
:
defaultLabel
;
const
localStorageKey
=
`
${
isAppPage
?
'
dapp-
'
:
''
}
wallet-connect-tooltip-shown`
;
return
{
defaultLabel
,
label
,
localStorageKey
};
},
[
router
.
pathname
]);
React
.
useEffect
(()
=>
{
React
.
useEffect
(()
=>
{
const
key
=
`wallet-connect-tooltip-shown-
${
isMobile
?
'
mobile
'
:
'
desktop
'
}
`
;
const
wasShown
=
window
.
localStorage
.
getItem
(
localStorageKey
);
const
wasShown
=
window
.
localStorage
.
getItem
(
key
);
if
(
!
isDisabled
&&
!
wasShown
)
{
if
(
!
wasShown
)
{
setIsTooltipShown
.
on
();
setIsTooltipShown
.
on
();
window
.
localStorage
.
setItem
(
key
,
'
true
'
);
window
.
localStorage
.
setItem
(
localStorageKey
,
'
true
'
);
setTimeout
(()
=>
setIsTooltipShown
.
off
(),
3000
);
}
}
},
[
setIsTooltipShown
,
isMobile
]);
},
[
setIsTooltipShown
,
localStorageKey
,
isDisabled
]);
return
(
return
(
<
Tooltip
<
Tooltip
label=
{
<
span
>
Your wallet is used to interact with
<
br
/>
apps and contracts in the explorer
</
span
>
}
label=
{
isTooltipShown
?
label
:
defaultLabel
}
textAlign=
"center"
textAlign=
"center"
padding=
{
2
}
padding=
{
2
}
isDisabled=
{
isDisabled
}
isDisabled=
{
isDisabled
}
openDelay=
{
3
00
}
openDelay=
{
5
00
}
isOpen=
{
isTooltipShown
||
(
isMobile
?
false
:
undefined
)
}
isOpen=
{
isTooltipShown
||
(
isMobile
?
false
:
undefined
)
}
onClose=
{
setIsTooltipShown
.
off
}
onClose=
{
setIsTooltipShown
.
off
}
display=
{
isMobile
?
{
base
:
'
flex
'
,
lg
:
'
none
'
}
:
{
base
:
'
none
'
,
lg
:
'
flex
'
}
}
display=
{
isMobile
?
{
base
:
'
flex
'
,
lg
:
'
none
'
}
:
{
base
:
'
none
'
,
lg
:
'
flex
'
}
}
ref=
{
ref
}
>
>
{
children
}
{
children
}
</
Tooltip
>
</
Tooltip
>
...
...
ui/tokens/TokensTableItem.tsx
View file @
1484bb36
...
@@ -51,7 +51,13 @@ const TokensTableItem = ({
...
@@ -51,7 +51,13 @@ const TokensTableItem = ({
};
};
return
(
return
(
<
Tr
>
<
Tr
sx=
{
{
'
&:hover [aria-label="Add token to wallet"]
'
:
{
opacity
:
1
,
},
}
}
>
<
Td
>
<
Td
>
<
Flex
alignItems=
"flex-start"
>
<
Flex
alignItems=
"flex-start"
>
<
Skeleton
<
Skeleton
...
@@ -81,7 +87,12 @@ const TokensTableItem = ({
...
@@ -81,7 +87,12 @@ const TokensTableItem = ({
fontSize=
"sm"
fontSize=
"sm"
fontWeight=
{
500
}
fontWeight=
{
500
}
/>
/>
<
AddressAddToWallet
token=
{
token
}
isLoading=
{
isLoading
}
iconSize=
{
5
}
/>
<
AddressAddToWallet
token=
{
token
}
isLoading=
{
isLoading
}
iconSize=
{
5
}
opacity=
{
0
}
/>
</
Flex
>
</
Flex
>
<
Flex
columnGap=
{
1
}
>
<
Flex
columnGap=
{
1
}
>
<
Tag
isLoading=
{
isLoading
}
>
{
type
}
</
Tag
>
<
Tag
isLoading=
{
isLoading
}
>
{
type
}
</
Tag
>
...
...
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