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
bde9906e
Commit
bde9906e
authored
Jun 18, 2024
by
Max Alekseenko
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add security score to dapp card
parent
11fe8c6f
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
275 additions
and
869 deletions
+275
-869
marketplace.ts
types/client/marketplace.ts
+0
-5
AppSecurityReport.tsx
ui/marketplace/AppSecurityReport.tsx
+41
-12
ContractListButton.tsx
ui/marketplace/ContractListButton.tsx
+0
-66
ContractListModal.tsx
ui/marketplace/ContractListModal.tsx
+1
-1
ContractSecurityReport.tsx
ui/marketplace/ContractSecurityReport.tsx
+10
-2
MarketplaceAppCard.tsx
ui/marketplace/MarketplaceAppCard.tsx
+93
-87
MarketplaceAppIntegrationIcon.tsx
ui/marketplace/MarketplaceAppIntegrationIcon.tsx
+1
-1
MarketplaceAppModal.tsx
ui/marketplace/MarketplaceAppModal.tsx
+93
-115
MarketplaceAppTopBar.tsx
ui/marketplace/MarketplaceAppTopBar.tsx
+11
-5
MarketplaceList.tsx
ui/marketplace/MarketplaceList.tsx
+6
-3
MarketplaceListWithScores.tsx
ui/marketplace/MarketplaceListWithScores.tsx
+0
-86
AppLink.tsx
ui/marketplace/MarketplaceListWithScores/AppLink.tsx
+0
-74
ListItem.tsx
ui/marketplace/MarketplaceListWithScores/ListItem.tsx
+0
-125
MoreInfoButton.tsx
ui/marketplace/MarketplaceListWithScores/MoreInfoButton.tsx
+0
-29
Table.tsx
ui/marketplace/MarketplaceListWithScores/Table.tsx
+0
-52
TableItem.tsx
ui/marketplace/MarketplaceListWithScores/TableItem.tsx
+0
-126
useMarketplace.tsx
ui/marketplace/useMarketplace.tsx
+2
-17
Marketplace.tsx
ui/pages/Marketplace.tsx
+12
-60
SolidityscanReportButton.tsx
ui/shared/solidityscanReport/SolidityscanReportButton.tsx
+5
-3
No files found.
types/client/marketplace.ts
View file @
bde9906e
...
...
@@ -41,11 +41,6 @@ export enum ContractListTypes {
VERIFIED
=
'
Verified
'
,
}
export
enum
MarketplaceDisplayType
{
DEFAULT
=
'
default
'
,
SCORES
=
'
scores
'
,
}
export
type
MarketplaceAppSecurityReport
=
{
overallInfo
:
{
verifiedNumber
:
number
;
...
...
ui/marketplace/AppSecurityReport.tsx
View file @
bde9906e
import
{
Box
,
Text
,
Link
,
Popover
,
PopoverTrigger
,
PopoverBody
,
PopoverContent
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Text
,
Link
,
Popover
,
PopoverTrigger
,
PopoverBody
,
PopoverContent
,
useDisclosure
,
chakra
,
Flex
,
Divider
,
Icon
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
MarketplaceAppSecurityReport
}
from
'
types/client/marketplace
'
;
import
{
ContractListTypes
}
from
'
types/client/marketplace
'
;
import
config
from
'
configs/app
'
;
// This icon doesn't work properly when it is in the sprite
// Probably because of the gradient
// eslint-disable-next-line no-restricted-imports
import
solidityScanIcon
from
'
icons/brands/solidity_scan.svg
'
;
import
{
apos
}
from
'
lib/html-entities
'
;
import
*
as
mixpanel
from
'
lib/mixpanel/index
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
...
...
@@ -14,13 +19,17 @@ import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanRe
type
Props
=
{
id
:
string
;
securityReport
?:
MarketplaceAppSecurityReport
;
showContractList
:
()
=>
void
;
showContractList
:
(
id
:
string
,
type
:
ContractListTypes
)
=>
void
;
isLoading
?:
boolean
;
onlyIcon
?:
boolean
;
source
:
'
Security view
'
|
'
App modal
'
|
'
App page
'
;
source
:
'
Discovery view
'
|
'
App modal
'
|
'
App page
'
;
className
?:
string
;
popoverPlacement
?:
'
bottom-start
'
|
'
bottom-end
'
;
}
const
AppSecurityReport
=
({
id
,
securityReport
,
showContractList
,
isLoading
,
onlyIcon
,
source
}:
Props
)
=>
{
const
AppSecurityReport
=
({
id
,
securityReport
,
showContractList
,
isLoading
,
onlyIcon
,
source
,
className
,
popoverPlacement
=
'
bottom-start
'
,
}:
Props
)
=>
{
const
{
isOpen
,
onToggle
,
onClose
}
=
useDisclosure
();
const
handleButtonClick
=
React
.
useCallback
(()
=>
{
...
...
@@ -28,10 +37,15 @@ const AppSecurityReport = ({ id, securityReport, showContractList, isLoading, on
onToggle
();
},
[
id
,
source
,
onToggle
]);
const
handleLinkClick
=
React
.
useCallback
(()
=>
{
const
showAnalyzedContracts
=
React
.
useCallback
(()
=>
{
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
Analyzed contracts
'
,
Info
:
id
,
Source
:
'
Security score popup
'
});
showContractList
();
},
[
id
,
showContractList
]);
showContractList
(
id
,
ContractListTypes
.
ANALYZED
);
},
[
showContractList
,
id
]);
const
showAllContracts
=
React
.
useCallback
(()
=>
{
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
Total contracts
'
,
Info
:
id
,
Source
:
'
Security score popup
'
});
showContractList
(
id
,
ContractListTypes
.
ALL
);
},
[
showContractList
,
id
]);
if
(
!
securityReport
&&
!
isLoading
)
{
return
null
;
...
...
@@ -45,7 +59,7 @@ const AppSecurityReport = ({ id, securityReport, showContractList, isLoading, on
}
=
securityReport
?.
overallInfo
||
{};
return
(
<
Popover
isOpen=
{
isOpen
}
onClose=
{
onClose
}
placement=
"bottom-start"
isLazy
>
<
Popover
isOpen=
{
isOpen
}
onClose=
{
onClose
}
placement=
{
popoverPlacement
}
isLazy
>
<
PopoverTrigger
>
<
SolidityscanReportButton
score=
{
securityScore
}
...
...
@@ -54,13 +68,29 @@ const AppSecurityReport = ({ id, securityReport, showContractList, isLoading, on
isActive=
{
isOpen
}
onlyIcon=
{
onlyIcon
}
label=
"The security score is based on analysis of a DApp's smart contracts."
className=
{
className
}
/>
</
PopoverTrigger
>
<
PopoverContent
w=
{
{
base
:
'
100vw
'
,
lg
:
'
328px
'
}
}
>
<
PopoverBody
px=
"26px"
py=
"20px"
fontSize=
"sm"
>
<
Text
fontWeight=
"500"
fontSize=
"xs"
mb=
{
2
}
variant=
"secondary"
>
Smart contracts info
</
Text
>
<
Flex
alignItems=
"center"
justifyContent=
"space-between"
h=
"32px"
>
<
Flex
alignItems=
"center"
>
<
IconSvg
name=
"contracts_verified"
boxSize=
{
5
}
color=
"green.500"
mr=
{
1
}
/>
<
Text
>
Verified contracts
</
Text
>
</
Flex
>
<
Link
fontSize=
"sm"
fontWeight=
"500"
onClick=
{
showAllContracts
}
>
{
securityReport
?.
overallInfo
.
verifiedNumber
??
0
}
of
{
securityReport
?.
overallInfo
.
totalContractsNumber
??
0
}
</
Link
>
</
Flex
>
<
Divider
my=
{
3
}
/>
<
Box
mb=
{
5
}
>
{
solidityScanContractsNumber
}
smart contract
{
solidityScanContractsNumber
===
1
?
'
was
'
:
'
s were
'
}
evaluated to determine
this protocol
{
apos
}
s overall security score on the
{
config
.
chain
.
name
}
network.
this protocol
{
apos
}
s overall security score on the
{
config
.
chain
.
name
}
network by
{
'
'
}
<
Box
>
<
Icon
as=
{
solidityScanIcon
}
mr=
{
1
}
w=
"23px"
h=
"20px"
display=
"inline-block"
verticalAlign=
"middle"
/>
<
Text
fontWeight=
{
600
}
display=
"inline-block"
>
SolidityScan
</
Text
>
</
Box
>
</
Box
>
<
SolidityscanReportScore
score=
{
securityScore
}
mb=
{
5
}
/>
{
issueSeverityDistribution
&&
totalIssues
>
0
&&
(
...
...
@@ -69,9 +99,8 @@ const AppSecurityReport = ({ id, securityReport, showContractList, isLoading, on
<
SolidityscanReportDetails
vulnerabilities=
{
issueSeverityDistribution
}
vulnerabilitiesCount=
{
totalIssues
}
/>
</
Box
>
)
}
<
Link
onClick=
{
handleLinkClick
}
display=
"inline-flex"
alignItems=
"center"
>
<
Link
onClick=
{
showAnalyzedContracts
}
display=
"inline-flex"
alignItems=
"center"
>
Analyzed contracts
<
IconSvg
name=
"arrows/north-east"
boxSize=
{
5
}
color=
"gray.400"
/>
</
Link
>
</
PopoverBody
>
</
PopoverContent
>
...
...
@@ -79,4 +108,4 @@ const AppSecurityReport = ({ id, securityReport, showContractList, isLoading, on
);
};
export
default
AppSecurityReport
;
export
default
chakra
(
AppSecurityReport
)
;
ui/marketplace/ContractListButton.tsx
deleted
100644 → 0
View file @
11fe8c6f
import
{
Link
,
Tooltip
,
Skeleton
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
MouseEvent
}
from
'
react
'
;
import
config
from
'
configs/app
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
export
enum
ContractListButtonVariants
{
ALL_CONTRACTS
=
'
all contracts
'
,
VERIFIED_CONTRACTS
=
'
verified contracts
'
,
}
const
values
=
{
[
ContractListButtonVariants
.
ALL_CONTRACTS
]:
{
icon
:
'
contracts
'
as
const
,
iconColor
:
'
gray.500
'
,
tooltip
:
`Total number of contracts deployed by the protocol on
${
config
.
chain
.
name
}
`
,
},
[
ContractListButtonVariants
.
VERIFIED_CONTRACTS
]:
{
icon
:
'
contracts_verified
'
as
const
,
iconColor
:
'
green.500
'
,
tooltip
:
`Number of verified contracts on
${
config
.
chain
.
name
}
`
,
},
};
interface
Props
{
children
:
string
|
number
;
onClick
:
(
event
:
MouseEvent
)
=>
void
;
variant
:
ContractListButtonVariants
;
isLoading
?:
boolean
;
}
const
ContractListButton
=
({
children
,
onClick
,
variant
,
isLoading
}:
Props
)
=>
{
const
{
icon
,
iconColor
,
tooltip
}
=
values
[
variant
];
return
(
<
Tooltip
label=
{
tooltip
}
textAlign=
"center"
padding=
{
2
}
isDisabled=
{
!
tooltip
}
openDelay=
{
500
}
width=
"250px"
>
<
Skeleton
isLoaded=
{
!
isLoading
}
display=
"inline-flex"
alignItems=
"center"
width=
{
isLoading
?
'
40px
'
:
'
auto
'
}
height=
"30px"
borderRadius=
"base"
>
<
Link
fontSize=
"sm"
onClick=
{
onClick
}
fontWeight=
"500"
display=
"inline-flex"
>
{
icon
&&
<
IconSvg
name=
{
icon
}
boxSize=
{
5
}
color=
{
iconColor
}
mr=
{
1
}
/>
}
{
children
}
</
Link
>
</
Skeleton
>
</
Tooltip
>
);
};
export
default
ContractListButton
;
ui/marketplace/ContractListModal.tsx
View file @
bde9906e
...
...
@@ -37,7 +37,7 @@ const ContractListModal = ({ onClose, onBack, type, contracts }: Props) => {
switch
(
type
)
{
default
:
case
ContractListTypes
.
ALL
:
return
contracts
;
return
contracts
.
sort
((
a
)
=>
a
.
isVerified
?
-
1
:
1
)
;
case
ContractListTypes
.
ANALYZED
:
return
contracts
.
filter
((
contract
)
=>
Boolean
(
contract
.
solidityScanReport
))
...
...
ui/marketplace/ContractSecurityReport.tsx
View file @
bde9906e
import
{
Box
,
Text
,
Popover
,
PopoverTrigger
,
PopoverBody
,
PopoverContent
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Text
,
Popover
,
PopoverTrigger
,
PopoverBody
,
PopoverContent
,
useDisclosure
,
Icon
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
SolidityscanReport
}
from
'
types/api/contract
'
;
import
config
from
'
configs/app
'
;
// This icon doesn't work properly when it is in the sprite
// Probably because of the gradient
// eslint-disable-next-line no-restricted-imports
import
solidityScanIcon
from
'
icons/brands/solidity_scan.svg
'
;
import
*
as
mixpanel
from
'
lib/mixpanel/index
'
;
import
LinkExternal
from
'
ui/shared/links/LinkExternal
'
;
import
SolidityscanReportButton
from
'
ui/shared/solidityscanReport/SolidityscanReportButton
'
;
...
...
@@ -46,7 +50,11 @@ const ContractSecurityReport = ({ securityReport }: Props) => {
<
PopoverContent
w=
{
{
base
:
'
100vw
'
,
lg
:
'
328px
'
}
}
>
<
PopoverBody
px=
"26px"
py=
"20px"
fontSize=
"sm"
>
<
Box
mb=
{
5
}
>
The security score was derived from evaluating the smart contracts of a protocol on the
{
config
.
chain
.
name
}
network.
The security score was derived from evaluating the smart contracts of a protocol on the
{
config
.
chain
.
name
}
network by
{
'
'
}
<
Box
>
<
Icon
as=
{
solidityScanIcon
}
mr=
{
1
}
w=
"23px"
h=
"20px"
display=
"inline-block"
verticalAlign=
"middle"
/>
<
Text
fontWeight=
{
600
}
display=
"inline-block"
>
SolidityScan
</
Text
>
</
Box
>
</
Box
>
<
SolidityscanReportScore
score=
{
parseFloat
(
securityScore
)
}
mb=
{
5
}
/>
{
issueSeverityDistribution
&&
totalIssues
>
0
&&
(
...
...
ui/marketplace/MarketplaceAppCard.tsx
View file @
bde9906e
...
...
@@ -2,20 +2,23 @@ import { Box, IconButton, Image, Link, LinkBox, Skeleton, useColorModeValue, cha
import
type
{
MouseEvent
}
from
'
react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
MarketplaceApp
Preview
}
from
'
types/client/marketplace
'
;
import
type
{
MarketplaceApp
WithSecurityReport
,
ContractListTypes
}
from
'
types/client/marketplace
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
AppSecurityReport
from
'
./AppSecurityReport
'
;
import
MarketplaceAppCardLink
from
'
./MarketplaceAppCardLink
'
;
import
MarketplaceAppIntegrationIcon
from
'
./MarketplaceAppIntegrationIcon
'
;
interface
Props
extends
MarketplaceApp
Preview
{
interface
Props
extends
MarketplaceApp
WithSecurityReport
{
onInfoClick
:
(
id
:
string
)
=>
void
;
isFavorite
:
boolean
;
onFavoriteClick
:
(
id
:
string
,
isFavorite
:
boolean
)
=>
void
;
isLoading
:
boolean
;
onAppClick
:
(
event
:
MouseEvent
,
id
:
string
)
=>
void
;
className
?:
string
;
showContractList
:
(
id
:
string
,
type
:
ContractListTypes
)
=>
void
;
}
const
MarketplaceAppCard
=
({
...
...
@@ -33,8 +36,11 @@ const MarketplaceAppCard = ({
isLoading
,
internalWallet
,
onAppClick
,
securityReport
,
className
,
showContractList
,
}:
Props
)
=>
{
const
isMobile
=
useIsMobile
();
const
categoriesLabel
=
categories
.
join
(
'
,
'
);
const
handleInfoClick
=
useCallback
((
event
:
MouseEvent
)
=>
{
...
...
@@ -64,26 +70,23 @@ const MarketplaceAppCard = ({
role=
"group"
>
<
Flex
flexDirection=
{
{
base
:
'
row
'
,
sm
:
'
column
'
}
}
flexDirection=
"column"
height=
"100%"
alignContent=
"start"
gap=
{
{
base
:
4
,
sm
:
0
}
}
gap=
{
2
}
>
<
Flex
display=
{
{
base
:
'
flex
'
,
sm
:
'
contents
'
}
}
flexDirection=
"column"
alignItems=
"center"
justifyContent=
"space-between"
gap=
{
4
}
>
<
Skeleton
isLoaded=
{
!
isLoading
}
marginBottom=
{
4
}
w=
{
{
base
:
'
64px
'
,
sm
:
'
96px
'
}
}
h=
{
{
base
:
'
64px
'
,
sm
:
'
96px
'
}
}
display=
"flex"
alignItems=
"center"
justifyContent=
"center"
order=
{
{
base
:
'
auto
'
,
sm
:
1
}
}
mb=
{
{
base
:
0
,
sm
:
2
}
}
>
<
Image
src=
{
isLoading
?
undefined
:
logoUrl
}
...
...
@@ -92,93 +95,96 @@ const MarketplaceAppCard = ({
/>
</
Skeleton
>
{
!
isLoading
&&
(
<
Box
display=
"flex"
marginTop=
{
{
base
:
0
,
sm
:
'
auto
'
}
}
paddingTop=
{
{
base
:
0
,
sm
:
4
}
}
order=
{
{
base
:
'
auto
'
,
sm
:
5
}
}
<
Flex
display=
{
{
base
:
'
flex
'
,
sm
:
'
contents
'
}
}
flexDirection=
"column"
gap=
{
2
}
pt=
{
1
}
>
<
Skeleton
isLoaded=
{
!
isLoading
}
fontSize=
{
{
base
:
'
sm
'
,
sm
:
'
lg
'
}
}
lineHeight=
{
{
base
:
'
20px
'
,
sm
:
'
28px
'
}
}
paddingRight=
{
{
base
:
'
40px
'
,
sm
:
0
}
}
fontWeight=
"semibold"
fontFamily=
"heading"
display=
"inline-block"
>
<
MarketplaceAppCardLink
id=
{
id
}
url=
{
url
}
external=
{
external
}
title=
{
title
}
onClick=
{
onAppClick
}
/>
<
MarketplaceAppIntegrationIcon
external=
{
external
}
internalWallet=
{
internalWallet
}
/>
</
Skeleton
>
<
Skeleton
isLoaded=
{
!
isLoading
}
color=
"text_secondary"
fontSize=
"xs"
lineHeight=
"16px"
>
<
Link
fontSize=
{
{
base
:
'
xs
'
,
sm
:
'
sm
'
}
}
fontWeight=
"500"
paddingRight=
{
{
sm
:
2
}
}
href=
"#"
onClick=
{
handleInfoClick
}
>
More info
</
Link
>
</
Box
>
)
}
<
span
>
{
categoriesLabel
}
</
span
>
</
Skeleton
>
</
Flex
>
</
Flex
>
<
Flex
display=
{
{
base
:
'
flex
'
,
sm
:
'
contents
'
}
}
flexDirection=
"column"
gap=
{
2
}
<
Skeleton
isLoaded=
{
!
isLoading
}
fontSize=
"sm"
lineHeight=
"20px"
noOfLines=
{
{
base
:
2
,
sm
:
3
}
}
>
<
Skeleton
isLoaded=
{
!
isLoading
}
marginBottom=
{
{
base
:
0
,
sm
:
2
}
}
fontSize=
{
{
base
:
'
sm
'
,
sm
:
'
lg
'
}
}
lineHeight=
{
{
base
:
'
20px
'
,
sm
:
'
28px
'
}
}
paddingRight=
{
{
base
:
'
25px
'
,
sm
:
0
}
}
fontWeight=
"semibold"
fontFamily=
"heading"
display=
"inline-block"
order=
{
{
base
:
'
auto
'
,
sm
:
2
}
}
>
<
MarketplaceAppCardLink
id=
{
id
}
url=
{
url
}
external=
{
external
}
title=
{
title
}
onClick=
{
onAppClick
}
/>
<
MarketplaceAppIntegrationIcon
external=
{
external
}
internalWallet=
{
internalWallet
}
/>
</
Skeleton
>
<
Skeleton
isLoaded=
{
!
isLoading
}
marginBottom=
{
{
base
:
0
,
sm
:
2
}
}
color=
"text_secondary"
fontSize=
"xs"
lineHeight=
"16px"
order=
{
{
base
:
'
auto
'
,
sm
:
3
}
}
>
<
span
>
{
categoriesLabel
}
</
span
>
</
Skeleton
>
<
Skeleton
isLoaded=
{
!
isLoading
}
fontSize=
{
{
base
:
'
xs
'
,
sm
:
'
sm
'
}
}
lineHeight=
"20px"
noOfLines=
{
3
}
order=
{
{
base
:
'
auto
'
,
sm
:
4
}
}
>
{
shortDescription
}
</
Skeleton
>
</
Flex
>
{
shortDescription
}
</
Skeleton
>
{
!
isLoading
&&
(
<
IconButton
<
Box
display=
"flex"
alignItems=
"center"
justifyContent=
"center"
justifyContent=
"space-between"
marginTop=
"auto"
>
<
Link
fontSize=
"sm"
fontWeight=
"500"
paddingRight=
{
{
sm
:
2
}
}
href=
"#"
onClick=
{
handleInfoClick
}
>
More info
</
Link
>
<
IconButton
aria
-
label=
"Mark as favorite"
title=
"Mark as favorite"
variant=
"ghost"
colorScheme=
"gray"
w=
{
{
base
:
6
,
sm
:
'
30px
'
}
}
h=
{
{
base
:
6
,
sm
:
'
30px
'
}
}
onClick=
{
handleFavoriteClick
}
icon=
{
isFavorite
?
<
IconSvg
name=
"star_filled"
w=
{
5
}
h=
{
5
}
color=
"yellow.400"
/>
:
<
IconSvg
name=
"star_outline"
w=
{
5
}
h=
{
5
}
color=
"gray.400"
/>
}
/>
</
Box
>
)
}
{
securityReport
&&
(
<
AppSecurityReport
id=
{
id
}
securityReport=
{
securityReport
}
showContractList=
{
showContractList
}
isLoading=
{
isLoading
}
source=
"Discovery view"
popoverPlacement=
{
isMobile
?
'
bottom-end
'
:
'
bottom-start
'
}
position=
"absolute"
right=
{
{
base
:
1
,
sm
:
'
10px
'
}
}
top=
{
{
base
:
1
,
sm
:
'
10px
'
}
}
aria
-
label=
"Mark as favorite"
title=
"Mark as favorite"
variant=
"ghost"
colorScheme=
"gray"
w=
{
9
}
h=
{
8
}
onClick=
{
handleFavoriteClick
}
icon=
{
isFavorite
?
<
IconSvg
name=
"star_filled"
w=
{
5
}
h=
{
5
}
color=
"yellow.400"
/>
:
<
IconSvg
name=
"star_outline"
w=
{
5
}
h=
{
5
}
color=
"gray.400"
/>
}
right=
{
{
base
:
3
,
sm
:
5
}
}
top=
{
{
base
:
'
10px
'
,
sm
:
5
}
}
border=
{
0
}
padding=
{
0
}
/>
)
}
</
Flex
>
...
...
ui/marketplace/MarketplaceAppIntegrationIcon.tsx
View file @
bde9906e
...
...
@@ -42,7 +42,7 @@ const MarketplaceAppIntegrationIcon = ({ external, internalWallet }: Props) => {
position=
"relative"
cursor=
"pointer"
verticalAlign=
"middle"
mb=
{
1
}
mb=
{
{
base
:
0
,
sm
:
1
}
}
/>
</
Tooltip
>
);
...
...
ui/marketplace/MarketplaceAppModal.tsx
View file @
bde9906e
import
{
Box
,
Flex
,
Heading
,
IconButton
,
Image
,
Link
,
List
,
Modal
,
ModalBody
,
Box
,
Flex
,
Heading
,
IconButton
,
Image
,
Link
,
Modal
,
ModalBody
,
ModalCloseButton
,
ModalContent
,
ModalFooter
,
ModalOverlay
,
Tag
,
Text
,
useColorModeValue
,
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
...
...
@@ -14,7 +14,6 @@ import type { IconName } from 'ui/shared/IconSvg';
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
AppSecurityReport
from
'
./AppSecurityReport
'
;
import
ContractListButton
,
{
ContractListButtonVariants
}
from
'
./ContractListButton
'
;
import
MarketplaceAppModalLink
from
'
./MarketplaceAppModalLink
'
;
type
Props
=
{
...
...
@@ -79,25 +78,16 @@ const MarketplaceAppModal = ({
onFavoriteClick
(
id
,
isFavorite
,
'
App modal
'
);
},
[
onFavoriteClick
,
id
,
isFavorite
]);
const
showContractList
=
useCallback
((
type
:
ContractListTypes
)
=>
{
const
showContractList
=
useCallback
((
id
:
string
,
type
:
ContractListTypes
)
=>
{
onClose
();
showContractListProp
(
id
,
type
,
true
);
},
[
onClose
,
showContractListProp
,
id
]);
},
[
onClose
,
showContractListProp
]);
const
showAllContracts
=
React
.
useCallback
(()
=>
{
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
Total contracts
'
,
Info
:
id
,
Source
:
'
App modal
'
});
showContractList
(
ContractListTypes
.
ALL
);
showContractList
(
id
,
ContractListTypes
.
ALL
);
},
[
showContractList
,
id
]);
const
showVerifiedContracts
=
React
.
useCallback
(()
=>
{
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
Verified contracts
'
,
Info
:
id
,
Source
:
'
App modal
'
});
showContractList
(
ContractListTypes
.
VERIFIED
);
},
[
showContractList
,
id
]);
const
showAnalyzedContracts
=
React
.
useCallback
(()
=>
{
showContractList
(
ContractListTypes
.
ANALYZED
);
},
[
showContractList
]);
const
isMobile
=
useIsMobile
();
const
logoUrl
=
useColorModeValue
(
logo
,
logoDarkMode
||
logo
);
...
...
@@ -185,126 +175,114 @@ const MarketplaceAppModal = ({
<
IconSvg
name=
"star_outline"
w=
{
5
}
h=
{
5
}
color=
{
starOutlineIconColor
}
/>
}
/>
</
Flex
>
{
securityReport
&&
(
<
Flex
alignItems=
"center"
gap=
{
3
}
>
<
AppSecurityReport
id=
{
id
}
securityReport=
{
securityReport
}
showContractList=
{
showAnalyzedContracts
}
source=
"App modal"
/>
<
ContractListButton
onClick=
{
showAllContracts
}
variant=
{
ContractListButtonVariants
.
ALL_CONTRACTS
}
>
{
securityReport
.
overallInfo
.
totalContractsNumber
}
</
ContractListButton
>
<
ContractListButton
onClick=
{
showVerifiedContracts
}
variant=
{
ContractListButtonVariants
.
VERIFIED_CONTRACTS
}
>
{
securityReport
.
overallInfo
.
verifiedNumber
}
</
ContractListButton
>
</
Flex
>
)
}
</
Flex
>
</
Box
>
</
Box
>
<
ModalCloseButton
/>
<
ModalBody
>
<
Heading
as=
"h3"
fontSize=
"2xl"
marginBottom=
{
4
}
>
Overview
</
Heading
>
<
ModalBody
mb=
{
6
}
>
{
securityReport
&&
(
<
Flex
direction=
{
{
base
:
'
column
'
,
sm
:
'
row
'
}
}
justifyContent=
{
{
base
:
'
flex-start
'
,
sm
:
'
space-between
'
}
}
gap=
{
3
}
fontSize=
"sm"
mb=
{
6
}
>
<
Flex
alignItems=
"center"
gap=
{
2
}
flexWrap=
"wrap"
>
<
IconSvg
name=
"contracts_verified"
boxSize=
{
5
}
color=
"green.500"
/>
<
Text
>
Verified contracts
</
Text
>
<
Text
fontWeight=
"500"
>
{
securityReport
?.
overallInfo
.
verifiedNumber
??
0
}
of
{
securityReport
?.
overallInfo
.
totalContractsNumber
??
0
}
</
Text
>
<
Link
onClick=
{
showAllContracts
}
ml=
{
1
}
>
View all contracts
</
Link
>
</
Flex
>
<
Flex
alignItems=
"center"
gap=
{
2
}
>
<
Text
>
Security level
</
Text
>
<
AppSecurityReport
id=
{
id
}
securityReport=
{
securityReport
}
showContractList=
{
showContractList
}
source=
"App modal"
/>
</
Flex
>
</
Flex
>
)
}
<
Text
>
{
description
}
</
Text
>
</
ModalBody
>
<
Box
marginBottom=
{
2
}
>
<
ModalFooter
display=
"flex"
flexDirection=
{
{
base
:
'
column
'
,
sm
:
'
row
'
}
}
justifyContent=
{
{
base
:
'
flex-start
'
,
sm
:
'
space-between
'
}
}
alignItems=
{
{
base
:
'
flex-start
'
,
sm
:
'
center
'
}
}
gap=
{
3
}
>
<
Flex
gap=
{
2
}
>
{
categories
.
map
((
category
)
=>
(
<
Tag
colorScheme=
"blue"
marginRight=
{
2
}
marginBottom=
{
2
}
key=
{
category
}
>
{
category
}
</
Tag
>
))
}
</
Box
>
<
Text
>
{
description
}
</
Text
>
</
ModalBody
>
<
ModalFooter
display=
"flex"
flexDirection=
{
{
base
:
'
column
'
,
sm
:
'
row
'
}
}
alignItems=
{
{
base
:
'
flex-start
'
,
sm
:
'
center
'
}
}
>
{
site
&&
(
<
Link
isExternal
href=
{
site
}
display=
"flex"
alignItems=
"center"
paddingRight=
{
{
sm
:
2
}
}
marginBottom=
{
{
base
:
3
,
sm
:
0
}
}
maxW=
"100%"
overflow=
"hidden"
>
<
IconSvg
name=
"link"
display=
"inline"
verticalAlign=
"baseline"
boxSize=
"18px"
marginRight=
{
2
}
/>
</
Flex
>
<
Text
color=
"inherit"
whiteSpace=
"nowrap"
overflow=
"hidden"
textOverflow=
"ellipsis"
<
Flex
alignItems=
"center"
gap=
{
3
}
>
{
site
&&
(
<
Link
isExternal
href=
{
site
}
display=
"flex"
alignItems=
"center"
fontSize=
"sm"
>
{
getHostname
(
site
)
}
</
Text
>
</
Link
>
)
}
<
IconSvg
name=
"link"
display=
"inline"
verticalAlign=
"baseline"
boxSize=
"18px"
marginRight=
{
2
}
/>
{
socialLinks
.
length
>
0
&&
(
<
List
marginLeft=
{
{
sm
:
'
auto
'
}
}
display=
"grid"
gridAutoFlow=
"column"
columnGap=
{
2
}
>
{
socialLinks
.
map
(({
icon
,
url
})
=>
(
<
Link
aria
-
label=
{
`Link to ${ url }`
}
title=
{
url
}
key=
{
url
}
href=
{
url
}
display=
"flex"
alignItems=
"center"
justifyContent=
"center"
isExternal
w=
{
10
}
h=
{
10
}
<
Text
color=
"inherit"
whiteSpace=
"nowrap"
overflow=
"hidden"
textOverflow=
"ellipsis"
>
<
IconSvg
name=
{
icon
}
w=
"20px"
h=
"20px"
display=
"block"
color=
"text_secondary"
/>
</
Link
>
))
}
</
List
>
)
}
{
getHostname
(
site
)
}
</
Text
>
</
Link
>
)
}
{
socialLinks
.
map
(({
icon
,
url
})
=>
(
<
Link
aria
-
label=
{
`Link to ${ url }`
}
title=
{
url
}
key=
{
url
}
href=
{
url
}
display=
"flex"
alignItems=
"center"
justifyContent=
"center"
isExternal
w=
{
5
}
h=
{
5
}
>
<
IconSvg
name=
{
icon
}
w=
"20px"
h=
"20px"
display=
"block"
color=
"text_secondary"
/>
</
Link
>
))
}
</
Flex
>
</
ModalFooter
>
</
ModalContent
>
</
Modal
>
...
...
ui/marketplace/MarketplaceAppTopBar.tsx
View file @
bde9906e
...
...
@@ -27,7 +27,8 @@ type Props = {
}
const
MarketplaceAppTopBar
=
({
data
,
isLoading
,
securityReport
}:
Props
)
=>
{
const
[
showContractList
,
setShowContractList
]
=
useBoolean
(
false
);
const
[
isContractListShown
,
setIsContractListShown
]
=
useBoolean
(
false
);
const
[
contractListType
,
setContractListType
]
=
React
.
useState
(
ContractListTypes
.
ALL
);
const
appProps
=
useAppContext
();
const
isMobile
=
useIsMobile
();
...
...
@@ -44,6 +45,11 @@ const MarketplaceAppTopBar = ({ data, isLoading, securityReport }: Props) => {
}
catch
(
err
)
{}
}
const
showContractList
=
React
.
useCallback
((
id
:
string
,
type
:
ContractListTypes
)
=>
{
setIsContractListShown
.
on
();
setContractListType
(
type
);
},
[
setIsContractListShown
]);
return
(
<>
<
Flex
alignItems=
"center"
flexWrap=
"wrap"
mb=
{
{
base
:
3
,
md
:
2
}
}
rowGap=
{
3
}
columnGap=
{
2
}
>
...
...
@@ -74,7 +80,7 @@ const MarketplaceAppTopBar = ({ data, isLoading, securityReport }: Props) => {
<
AppSecurityReport
id=
{
data
?.
id
||
''
}
securityReport=
{
securityReport
}
showContractList=
{
s
etShowContractList
.
on
}
showContractList=
{
s
howContractList
}
isLoading=
{
isLoading
}
onlyIcon=
{
isMobile
}
source=
"App page"
...
...
@@ -87,11 +93,11 @@ const MarketplaceAppTopBar = ({ data, isLoading, securityReport }: Props) => {
</
Flex
>
)
}
</
Flex
>
{
showContractList
&&
(
{
isContractListShown
&&
(
<
ContractListModal
type=
{
ContractListTypes
.
ANALYZED
}
type=
{
contractListType
}
contracts=
{
securityReport
?.
contractsData
}
onClose=
{
set
ShowContractList
.
off
}
onClose=
{
set
IsContractListShown
.
off
}
/>
)
}
</>
...
...
ui/marketplace/MarketplaceList.tsx
View file @
bde9906e
...
...
@@ -2,7 +2,7 @@ import { Grid } from '@chakra-ui/react';
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
MouseEvent
}
from
'
react
'
;
import
type
{
MarketplaceApp
Preview
}
from
'
types/client/marketplace
'
;
import
type
{
MarketplaceApp
WithSecurityReport
,
ContractListTypes
}
from
'
types/client/marketplace
'
;
import
*
as
mixpanel
from
'
lib/mixpanel/index
'
;
...
...
@@ -10,16 +10,17 @@ import EmptySearchResult from './EmptySearchResult';
import
MarketplaceAppCard
from
'
./MarketplaceAppCard
'
;
type
Props
=
{
apps
:
Array
<
MarketplaceApp
Preview
>
;
apps
:
Array
<
MarketplaceApp
WithSecurityReport
>
;
showAppInfo
:
(
id
:
string
)
=>
void
;
favoriteApps
:
Array
<
string
>
;
onFavoriteClick
:
(
id
:
string
,
isFavorite
:
boolean
,
source
:
'
Discovery view
'
)
=>
void
;
isLoading
:
boolean
;
selectedCategoryId
?:
string
;
onAppClick
:
(
event
:
MouseEvent
,
id
:
string
)
=>
void
;
showContractList
:
(
id
:
string
,
type
:
ContractListTypes
)
=>
void
;
}
const
MarketplaceList
=
({
apps
,
showAppInfo
,
favoriteApps
,
onFavoriteClick
,
isLoading
,
selectedCategoryId
,
onAppClick
}:
Props
)
=>
{
const
MarketplaceList
=
({
apps
,
showAppInfo
,
favoriteApps
,
onFavoriteClick
,
isLoading
,
selectedCategoryId
,
onAppClick
,
showContractList
}:
Props
)
=>
{
const
handleInfoClick
=
useCallback
((
id
:
string
)
=>
{
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
More button
'
,
Info
:
id
,
Source
:
'
Discovery view
'
});
showAppInfo
(
id
);
...
...
@@ -55,6 +56,8 @@ const MarketplaceList = ({ apps, showAppInfo, favoriteApps, onFavoriteClick, isL
isLoading=
{
isLoading
}
internalWallet=
{
app
.
internalWallet
}
onAppClick=
{
onAppClick
}
securityReport=
{
app
.
securityReport
}
showContractList=
{
showContractList
}
/>
))
}
</
Grid
>
...
...
ui/marketplace/MarketplaceListWithScores.tsx
deleted
100644 → 0
View file @
11fe8c6f
import
{
Hide
,
Show
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
MouseEvent
}
from
'
react
'
;
import
type
{
MarketplaceAppWithSecurityReport
,
ContractListTypes
}
from
'
types/client/marketplace
'
;
import
DataListDisplay
from
'
ui/shared/DataListDisplay
'
;
import
EmptySearchResult
from
'
./EmptySearchResult
'
;
import
ListItem
from
'
./MarketplaceListWithScores/ListItem
'
;
import
Table
from
'
./MarketplaceListWithScores/Table
'
;
interface
Props
{
apps
:
Array
<
MarketplaceAppWithSecurityReport
>
;
showAppInfo
:
(
id
:
string
)
=>
void
;
favoriteApps
:
Array
<
string
>
;
onFavoriteClick
:
(
id
:
string
,
isFavorite
:
boolean
,
source
:
'
Security view
'
)
=>
void
;
isLoading
:
boolean
;
selectedCategoryId
?:
string
;
onAppClick
:
(
event
:
MouseEvent
,
id
:
string
)
=>
void
;
showContractList
:
(
id
:
string
,
type
:
ContractListTypes
)
=>
void
;
}
const
MarketplaceListWithScores
=
({
apps
,
showAppInfo
,
favoriteApps
,
onFavoriteClick
,
isLoading
,
selectedCategoryId
,
onAppClick
,
showContractList
,
}:
Props
)
=>
{
const
displayedApps
=
React
.
useMemo
(()
=>
[
...
apps
].
sort
((
a
,
b
)
=>
{
if
(
!
a
.
securityReport
)
{
return
1
;
}
else
if
(
!
b
.
securityReport
)
{
return
-
1
;
}
return
b
.
securityReport
.
overallInfo
.
securityScore
-
a
.
securityReport
.
overallInfo
.
securityScore
;
}),
[
apps
]);
const
content
=
apps
.
length
>
0
?
(
<>
<
Show
below=
"lg"
ssr=
{
false
}
>
{
displayedApps
.
map
((
app
,
index
)
=>
(
<
ListItem
key=
{
app
.
id
+
(
isLoading
?
index
:
''
)
}
app=
{
app
}
onInfoClick=
{
showAppInfo
}
isFavorite=
{
favoriteApps
.
includes
(
app
.
id
)
}
onFavoriteClick=
{
onFavoriteClick
}
isLoading=
{
isLoading
}
onAppClick=
{
onAppClick
}
showContractList=
{
showContractList
}
/>
))
}
</
Show
>
<
Hide
below=
"lg"
ssr=
{
false
}
>
<
Table
apps=
{
displayedApps
}
isLoading=
{
isLoading
}
onAppClick=
{
onAppClick
}
favoriteApps=
{
favoriteApps
}
onFavoriteClick=
{
onFavoriteClick
}
onInfoClick=
{
showAppInfo
}
showContractList=
{
showContractList
}
/>
</
Hide
>
</>
)
:
null
;
return
apps
.
length
>
0
?
(
<
DataListDisplay
isError=
{
false
}
items=
{
apps
}
emptyText=
"No apps found."
content=
{
content
}
/>
)
:
(
<
EmptySearchResult
selectedCategoryId=
{
selectedCategoryId
}
favoriteApps=
{
favoriteApps
}
/>
);
};
export
default
MarketplaceListWithScores
;
ui/marketplace/MarketplaceListWithScores/AppLink.tsx
deleted
100644 → 0
View file @
11fe8c6f
import
{
Flex
,
Skeleton
,
LinkBox
,
Image
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
MouseEvent
}
from
'
react
'
;
import
type
{
MarketplaceAppPreview
}
from
'
types/client/marketplace
'
;
import
MarketplaceAppCardLink
from
'
../MarketplaceAppCardLink
'
;
import
MarketplaceAppIntegrationIcon
from
'
../MarketplaceAppIntegrationIcon
'
;
interface
Props
{
app
:
MarketplaceAppPreview
;
isLoading
:
boolean
|
undefined
;
onAppClick
:
(
event
:
MouseEvent
,
id
:
string
)
=>
void
;
isLarge
?:
boolean
;
}
const
AppLink
=
({
app
,
isLoading
,
onAppClick
,
isLarge
=
false
}:
Props
)
=>
{
const
{
id
,
url
,
external
,
title
,
logo
,
logoDarkMode
,
internalWallet
,
categories
}
=
app
;
const
logoUrl
=
useColorModeValue
(
logo
,
logoDarkMode
||
logo
);
const
categoriesLabel
=
categories
.
join
(
'
,
'
);
return
(
<
LinkBox
display=
"flex"
height=
"100%"
width=
"100%"
role=
"group"
alignItems=
"center"
mb=
{
isLarge
?
0
:
4
}
>
<
Skeleton
isLoaded=
{
!
isLoading
}
w=
{
isLarge
?
'
56px
'
:
'
48px
'
}
h=
{
isLarge
?
'
56px
'
:
'
48px
'
}
display=
"flex"
alignItems=
"center"
justifyContent=
"center"
mr=
{
isLarge
?
3
:
4
}
flexShrink=
{
0
}
>
<
Image
src=
{
isLoading
?
undefined
:
logoUrl
}
alt=
{
`${ title } app icon`
}
borderRadius=
"8px"
/>
</
Skeleton
>
<
Flex
direction=
"column"
>
<
Skeleton
isLoaded=
{
!
isLoading
}
marginBottom=
{
0
}
fontSize=
"sm"
fontWeight=
"semibold"
fontFamily=
"heading"
display=
"inline-block"
mb=
{
1
}
>
<
MarketplaceAppCardLink
id=
{
id
}
url=
{
url
}
external=
{
external
}
title=
{
title
}
onClick=
{
onAppClick
}
/>
<
MarketplaceAppIntegrationIcon
external=
{
external
}
internalWallet=
{
internalWallet
}
/>
</
Skeleton
>
<
Skeleton
isLoaded=
{
!
isLoading
}
color=
"text_secondary"
fontSize=
{
isLarge
?
'
sm
'
:
'
xs
'
}
>
<
span
>
{
categoriesLabel
}
</
span
>
</
Skeleton
>
</
Flex
>
</
LinkBox
>
);
};
export
default
AppLink
;
ui/marketplace/MarketplaceListWithScores/ListItem.tsx
deleted
100644 → 0
View file @
11fe8c6f
import
{
Flex
,
IconButton
,
Text
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
MouseEvent
}
from
'
react
'
;
import
type
{
MarketplaceAppWithSecurityReport
}
from
'
types/client/marketplace
'
;
import
{
ContractListTypes
}
from
'
types/client/marketplace
'
;
import
*
as
mixpanel
from
'
lib/mixpanel/index
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
ListItemMobile
from
'
ui/shared/ListItemMobile/ListItemMobile
'
;
import
AppSecurityReport
from
'
../AppSecurityReport
'
;
import
ContractListButton
,
{
ContractListButtonVariants
}
from
'
../ContractListButton
'
;
import
AppLink
from
'
./AppLink
'
;
import
MoreInfoButton
from
'
./MoreInfoButton
'
;
type
Props
=
{
app
:
MarketplaceAppWithSecurityReport
;
onInfoClick
:
(
id
:
string
)
=>
void
;
isFavorite
:
boolean
;
onFavoriteClick
:
(
id
:
string
,
isFavorite
:
boolean
,
source
:
'
Security view
'
)
=>
void
;
isLoading
:
boolean
;
onAppClick
:
(
event
:
MouseEvent
,
id
:
string
)
=>
void
;
showContractList
:
(
id
:
string
,
type
:
ContractListTypes
)
=>
void
;
}
const
ListItem
=
({
app
,
onInfoClick
,
isFavorite
,
onFavoriteClick
,
isLoading
,
onAppClick
,
showContractList
}:
Props
)
=>
{
const
{
id
,
securityReport
}
=
app
;
const
handleInfoClick
=
React
.
useCallback
((
event
:
MouseEvent
)
=>
{
event
.
preventDefault
();
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
More button
'
,
Info
:
id
,
Source
:
'
Security view
'
});
onInfoClick
(
id
);
},
[
onInfoClick
,
id
]);
const
handleFavoriteClick
=
React
.
useCallback
(()
=>
{
onFavoriteClick
(
id
,
isFavorite
,
'
Security view
'
);
},
[
onFavoriteClick
,
id
,
isFavorite
]);
const
showAllContracts
=
React
.
useCallback
(()
=>
{
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
Total contracts
'
,
Info
:
id
,
Source
:
'
Security view
'
});
showContractList
(
id
,
ContractListTypes
.
ALL
);
},
[
showContractList
,
id
]);
const
showVerifiedContracts
=
React
.
useCallback
(()
=>
{
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
Verified contracts
'
,
Info
:
id
,
Source
:
'
Security view
'
});
showContractList
(
id
,
ContractListTypes
.
VERIFIED
);
},
[
showContractList
,
id
]);
const
showAnalyzedContracts
=
React
.
useCallback
(()
=>
{
showContractList
(
id
,
ContractListTypes
.
ANALYZED
);
},
[
showContractList
,
id
]);
return
(
<
ListItemMobile
rowGap=
{
3
}
py=
{
3
}
_first=
{
{
borderTop
:
'
none
'
,
paddingTop
:
0
}
}
_last=
{
{
borderBottom
:
'
none
'
,
paddingBottom
:
0
}
}
>
<
Flex
direction=
"column"
justifyContent=
"stretch"
padding=
{
3
}
width=
"100%"
>
<
Flex
position=
"relative"
>
<
AppLink
app=
{
app
}
isLoading=
{
isLoading
}
onAppClick=
{
onAppClick
}
/>
{
!
isLoading
&&
(
<
IconButton
position=
"absolute"
right=
{
-
1
}
top=
{
-
1
}
aria
-
label=
"Mark as favorite"
title=
"Mark as favorite"
variant=
"ghost"
colorScheme=
"gray"
w=
{
9
}
h=
{
8
}
onClick=
{
handleFavoriteClick
}
icon=
{
isFavorite
?
<
IconSvg
name=
"star_filled"
w=
{
5
}
h=
{
5
}
color=
"yellow.400"
/>
:
<
IconSvg
name=
"star_outline"
w=
{
5
}
h=
{
5
}
color=
"gray.400"
/>
}
/>
)
}
</
Flex
>
<
Flex
alignItems=
"center"
>
<
Flex
flex=
{
1
}
gap=
{
3
}
alignItems=
"center"
>
{
(
securityReport
||
isLoading
)
?
(
<>
<
AppSecurityReport
id=
{
id
}
isLoading=
{
isLoading
}
securityReport=
{
securityReport
}
showContractList=
{
showAnalyzedContracts
}
source=
"Security view"
/>
<
ContractListButton
onClick=
{
showAllContracts
}
variant=
{
ContractListButtonVariants
.
ALL_CONTRACTS
}
isLoading=
{
isLoading
}
>
{
securityReport
?.
overallInfo
.
totalContractsNumber
??
0
}
</
ContractListButton
>
<
ContractListButton
onClick=
{
showVerifiedContracts
}
variant=
{
ContractListButtonVariants
.
VERIFIED_CONTRACTS
}
isLoading=
{
isLoading
}
>
{
securityReport
?.
overallInfo
.
verifiedNumber
??
0
}
</
ContractListButton
>
</>
)
:
(
<
Text
variant=
"secondary"
fontSize=
"sm"
fontWeight=
{
500
}
>
Data will be available soon
</
Text
>
)
}
</
Flex
>
<
MoreInfoButton
onClick=
{
handleInfoClick
}
isLoading=
{
isLoading
}
/>
</
Flex
>
</
Flex
>
</
ListItemMobile
>
);
};
export
default
ListItem
;
ui/marketplace/MarketplaceListWithScores/MoreInfoButton.tsx
deleted
100644 → 0
View file @
11fe8c6f
import
{
Link
,
Skeleton
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
MouseEvent
}
from
'
react
'
;
interface
Props
{
onClick
:
(
event
:
MouseEvent
)
=>
void
;
isLoading
?:
boolean
;
}
const
MoreInfoButton
=
({
onClick
,
isLoading
}:
Props
)
=>
(
<
Skeleton
isLoaded=
{
!
isLoading
}
display=
"inline-flex"
alignItems=
"center"
height=
"30px"
borderRadius=
"base"
>
<
Link
fontSize=
"sm"
onClick=
{
onClick
}
fontWeight=
"500"
display=
"inline-flex"
>
More info
</
Link
>
</
Skeleton
>
);
export
default
MoreInfoButton
;
ui/marketplace/MarketplaceListWithScores/Table.tsx
deleted
100644 → 0
View file @
11fe8c6f
import
{
Table
as
ChakraTable
,
Tbody
,
Th
,
Tr
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
MouseEvent
}
from
'
react
'
;
import
type
{
MarketplaceAppWithSecurityReport
,
ContractListTypes
}
from
'
types/client/marketplace
'
;
import
{
default
as
Thead
}
from
'
ui/shared/TheadSticky
'
;
import
TableItem
from
'
./TableItem
'
;
type
Props
=
{
apps
:
Array
<
MarketplaceAppWithSecurityReport
>
;
isLoading
?:
boolean
;
favoriteApps
:
Array
<
string
>
;
onFavoriteClick
:
(
id
:
string
,
isFavorite
:
boolean
,
source
:
'
Security view
'
)
=>
void
;
onAppClick
:
(
event
:
MouseEvent
,
id
:
string
)
=>
void
;
onInfoClick
:
(
id
:
string
)
=>
void
;
showContractList
:
(
id
:
string
,
type
:
ContractListTypes
)
=>
void
;
}
const
Table
=
({
apps
,
isLoading
,
favoriteApps
,
onFavoriteClick
,
onAppClick
,
onInfoClick
,
showContractList
}:
Props
)
=>
{
return
(
<
ChakraTable
>
<
Thead
top=
{
0
}
>
<
Tr
>
<
Th
w=
"5%"
></
Th
>
<
Th
w=
"40%"
>
App
</
Th
>
<
Th
w=
"15%"
>
Contracts score
</
Th
>
<
Th
w=
"10%"
>
Total
</
Th
>
<
Th
w=
"10%"
>
Verified
</
Th
>
<
Th
w=
"20%"
></
Th
>
</
Tr
>
</
Thead
>
<
Tbody
>
{
apps
.
map
((
app
,
index
)
=>
(
<
TableItem
key=
{
app
.
id
+
(
isLoading
?
index
:
''
)
}
app=
{
app
}
isLoading=
{
isLoading
}
isFavorite=
{
favoriteApps
.
includes
(
app
.
id
)
}
onFavoriteClick=
{
onFavoriteClick
}
onAppClick=
{
onAppClick
}
onInfoClick=
{
onInfoClick
}
showContractList=
{
showContractList
}
/>
))
}
</
Tbody
>
</
ChakraTable
>
);
};
export
default
Table
;
ui/marketplace/MarketplaceListWithScores/TableItem.tsx
deleted
100644 → 0
View file @
11fe8c6f
import
{
Td
,
Tr
,
IconButton
,
Skeleton
,
Text
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
MouseEvent
}
from
'
react
'
;
import
type
{
MarketplaceAppWithSecurityReport
}
from
'
types/client/marketplace
'
;
import
{
ContractListTypes
}
from
'
types/client/marketplace
'
;
import
*
as
mixpanel
from
'
lib/mixpanel/index
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
AppSecurityReport
from
'
../AppSecurityReport
'
;
import
ContractListButton
,
{
ContractListButtonVariants
}
from
'
../ContractListButton
'
;
import
AppLink
from
'
./AppLink
'
;
import
MoreInfoButton
from
'
./MoreInfoButton
'
;
type
Props
=
{
app
:
MarketplaceAppWithSecurityReport
;
isLoading
?:
boolean
;
isFavorite
:
boolean
;
onFavoriteClick
:
(
id
:
string
,
isFavorite
:
boolean
,
source
:
'
Security view
'
)
=>
void
;
onAppClick
:
(
event
:
MouseEvent
,
id
:
string
)
=>
void
;
onInfoClick
:
(
id
:
string
)
=>
void
;
showContractList
:
(
id
:
string
,
type
:
ContractListTypes
)
=>
void
;
}
const
TableItem
=
({
app
,
isLoading
,
isFavorite
,
onFavoriteClick
,
onAppClick
,
onInfoClick
,
showContractList
,
}:
Props
)
=>
{
const
{
id
,
securityReport
}
=
app
;
const
handleInfoClick
=
React
.
useCallback
((
event
:
MouseEvent
)
=>
{
event
.
preventDefault
();
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
More button
'
,
Info
:
id
,
Source
:
'
Security view
'
});
onInfoClick
(
id
);
},
[
onInfoClick
,
id
]);
const
handleFavoriteClick
=
React
.
useCallback
(()
=>
{
onFavoriteClick
(
id
,
isFavorite
,
'
Security view
'
);
},
[
onFavoriteClick
,
id
,
isFavorite
]);
const
showAllContracts
=
React
.
useCallback
(()
=>
{
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
Total contracts
'
,
Info
:
id
,
Source
:
'
Security view
'
});
showContractList
(
id
,
ContractListTypes
.
ALL
);
},
[
showContractList
,
id
]);
const
showVerifiedContracts
=
React
.
useCallback
(()
=>
{
mixpanel
.
logEvent
(
mixpanel
.
EventTypes
.
PAGE_WIDGET
,
{
Type
:
'
Verified contracts
'
,
Info
:
id
,
Source
:
'
Security view
'
});
showContractList
(
id
,
ContractListTypes
.
VERIFIED
);
},
[
showContractList
,
id
]);
const
showAnalyzedContracts
=
React
.
useCallback
(()
=>
{
showContractList
(
id
,
ContractListTypes
.
ANALYZED
);
},
[
showContractList
,
id
]);
return
(
<
Tr
>
<
Td
verticalAlign=
"middle"
px=
{
2
}
>
<
Skeleton
isLoaded=
{
!
isLoading
}
>
<
IconButton
aria
-
label=
"Mark as favorite"
title=
"Mark as favorite"
variant=
"ghost"
colorScheme=
"gray"
w=
{
9
}
h=
{
8
}
onClick=
{
handleFavoriteClick
}
icon=
{
isFavorite
?
<
IconSvg
name=
"star_filled"
w=
{
5
}
h=
{
5
}
color=
"yellow.400"
/>
:
<
IconSvg
name=
"star_outline"
w=
{
5
}
h=
{
5
}
color=
"gray.400"
/>
}
/>
</
Skeleton
>
</
Td
>
<
Td
verticalAlign=
"middle"
>
<
AppLink
app=
{
app
}
isLoading=
{
isLoading
}
onAppClick=
{
onAppClick
}
isLarge
/>
</
Td
>
{
(
securityReport
||
isLoading
)
?
(
<>
<
Td
verticalAlign=
"middle"
>
<
AppSecurityReport
id=
{
id
}
securityReport=
{
securityReport
}
showContractList=
{
showAnalyzedContracts
}
isLoading=
{
isLoading
}
source=
"Security view"
/>
</
Td
>
<
Td
verticalAlign=
"middle"
>
<
ContractListButton
onClick=
{
showAllContracts
}
variant=
{
ContractListButtonVariants
.
ALL_CONTRACTS
}
isLoading=
{
isLoading
}
>
{
securityReport
?.
overallInfo
.
totalContractsNumber
??
0
}
</
ContractListButton
>
</
Td
>
<
Td
verticalAlign=
"middle"
>
<
ContractListButton
onClick=
{
showVerifiedContracts
}
variant=
{
ContractListButtonVariants
.
VERIFIED_CONTRACTS
}
isLoading=
{
isLoading
}
>
{
securityReport
?.
overallInfo
.
verifiedNumber
??
0
}
</
ContractListButton
>
</
Td
>
</>
)
:
(
<
Td
verticalAlign=
"middle"
colSpan=
{
3
}
>
<
Text
variant=
"secondary"
fontSize=
"sm"
fontWeight=
{
500
}
>
Data will be available soon
</
Text
>
</
Td
>
)
}
<
Td
verticalAlign=
"middle"
isNumeric
>
<
MoreInfoButton
onClick=
{
handleInfoClick
}
isLoading=
{
isLoading
}
/>
</
Td
>
</
Tr
>
);
};
export
default
TableItem
;
ui/marketplace/useMarketplace.tsx
View file @
bde9906e
...
...
@@ -3,7 +3,7 @@ import { useRouter } from 'next/router';
import
React
from
'
react
'
;
import
type
{
ContractListTypes
}
from
'
types/client/marketplace
'
;
import
{
MarketplaceCategory
,
MarketplaceDisplayType
}
from
'
types/client/marketplace
'
;
import
{
MarketplaceCategory
}
from
'
types/client/marketplace
'
;
import
useDebounce
from
'
lib/hooks/useDebounce
'
;
import
*
as
mixpanel
from
'
lib/mixpanel/index
'
;
...
...
@@ -26,15 +26,9 @@ export default function useMarketplace() {
const
router
=
useRouter
();
const
defaultCategoryId
=
getQueryParamString
(
router
.
query
.
category
);
const
defaultFilterQuery
=
getQueryParamString
(
router
.
query
.
filter
);
const
defaultDisplayType
=
getQueryParamString
(
router
.
query
.
tab
);
const
[
selectedAppId
,
setSelectedAppId
]
=
React
.
useState
<
string
|
null
>
(
null
);
const
[
selectedCategoryId
,
setSelectedCategoryId
]
=
React
.
useState
<
string
>
(
MarketplaceCategory
.
ALL
);
const
[
selectedDisplayType
,
setSelectedDisplayType
]
=
React
.
useState
<
string
>
(
Object
.
values
(
MarketplaceDisplayType
).
includes
(
defaultDisplayType
as
MarketplaceDisplayType
)
?
defaultDisplayType
:
MarketplaceDisplayType
.
DEFAULT
,
);
const
[
filterQuery
,
setFilterQuery
]
=
React
.
useState
(
defaultFilterQuery
);
const
[
favoriteApps
,
setFavoriteApps
]
=
React
.
useState
<
Array
<
string
>>
([]);
const
[
isFavoriteAppsLoaded
,
setIsFavoriteAppsLoaded
]
=
React
.
useState
<
boolean
>
(
false
);
...
...
@@ -91,10 +85,6 @@ export default function useMarketplace() {
setSelectedCategoryId
(
newCategory
);
},
[]);
const
handleDisplayTypeChange
=
React
.
useCallback
((
newDisplayType
:
MarketplaceDisplayType
)
=>
{
setSelectedDisplayType
(
newDisplayType
);
},
[]);
const
{
isPlaceholderData
,
isError
,
error
,
data
,
displayedApps
,
}
=
useMarketplaceApps
(
debouncedFilterQuery
,
selectedCategoryId
,
favoriteApps
,
isFavoriteAppsLoaded
);
...
...
@@ -120,7 +110,6 @@ export default function useMarketplace() {
const
query
=
_pickBy
({
category
:
selectedCategoryId
===
MarketplaceCategory
.
ALL
?
undefined
:
selectedCategoryId
,
filter
:
debouncedFilterQuery
,
tab
:
selectedDisplayType
===
MarketplaceDisplayType
.
DEFAULT
?
undefined
:
selectedDisplayType
,
},
Boolean
);
if
(
debouncedFilterQuery
.
length
>
0
)
{
...
...
@@ -135,7 +124,7 @@ export default function useMarketplace() {
// omit router in the deps because router.push() somehow modifies it
// and we get infinite re-renders then
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
debouncedFilterQuery
,
selectedCategoryId
,
selectedDisplayType
]);
},
[
debouncedFilterQuery
,
selectedCategoryId
]);
return
React
.
useMemo
(()
=>
({
selectedCategoryId
,
...
...
@@ -160,8 +149,6 @@ export default function useMarketplace() {
isCategoriesPlaceholderData
,
showContractList
,
contractListModalType
,
selectedDisplayType
,
onDisplayTypeChange
:
handleDisplayTypeChange
,
hasPreviousStep
,
}),
[
selectedCategoryId
,
...
...
@@ -184,8 +171,6 @@ export default function useMarketplace() {
isCategoriesPlaceholderData
,
showContractList
,
contractListModalType
,
selectedDisplayType
,
handleDisplayTypeChange
,
hasPreviousStep
,
]);
}
ui/pages/Marketplace.tsx
View file @
bde9906e
import
{
Box
,
Menu
,
MenuButton
,
MenuItem
,
MenuList
,
Flex
,
IconButton
,
Skeleton
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Menu
,
MenuButton
,
MenuItem
,
MenuList
,
Flex
,
IconButton
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
MouseEvent
}
from
'
react
'
;
import
{
MarketplaceCategory
,
MarketplaceDisplayType
}
from
'
types/client/marketplace
'
;
import
{
MarketplaceCategory
}
from
'
types/client/marketplace
'
;
import
type
{
TabItem
}
from
'
ui/shared/Tabs/types
'
;
import
config
from
'
configs/app
'
;
...
...
@@ -13,13 +13,11 @@ import ContractListModal from 'ui/marketplace/ContractListModal';
import
MarketplaceAppModal
from
'
ui/marketplace/MarketplaceAppModal
'
;
import
MarketplaceDisclaimerModal
from
'
ui/marketplace/MarketplaceDisclaimerModal
'
;
import
MarketplaceList
from
'
ui/marketplace/MarketplaceList
'
;
import
MarketplaceListWithScores
from
'
ui/marketplace/MarketplaceListWithScores
'
;
import
FilterInput
from
'
ui/shared/filters/FilterInput
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
type
{
IconName
}
from
'
ui/shared/IconSvg
'
;
import
LinkExternal
from
'
ui/shared/links/LinkExternal
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
import
RadioButtonGroup
from
'
ui/shared/radioButtonGroup/RadioButtonGroup
'
;
import
TabsWithScroll
from
'
ui/shared/Tabs/TabsWithScroll
'
;
import
useMarketplace
from
'
../marketplace/useMarketplace
'
;
...
...
@@ -67,8 +65,6 @@ const Marketplace = () => {
isCategoriesPlaceholderData
,
showContractList
,
contractListModalType
,
selectedDisplayType
,
onDisplayTypeChange
,
hasPreviousStep
,
}
=
useMarketplace
();
...
...
@@ -187,38 +183,6 @@ const Marketplace = () => {
</
Box
>
<
Flex
direction=
{
{
base
:
'
column
'
,
lg
:
'
row
'
}
}
mb=
{
{
base
:
4
,
lg
:
6
}
}
gap=
{
{
base
:
4
,
lg
:
3
}
}
>
{
feature
.
securityReportsUrl
&&
(
<
Skeleton
isLoaded=
{
!
isPlaceholderData
}
>
<
RadioButtonGroup
<
MarketplaceDisplayType
>
onChange=
{
onDisplayTypeChange
}
defaultValue=
{
selectedDisplayType
}
name="type"
options=
{
[
{
title
:
'
Discovery
'
,
value
:
MarketplaceDisplayType
.
DEFAULT
,
icon
:
'
apps_xs
'
,
onlyIcon
:
false
,
},
{
title
:
'
Apps scores
'
,
value
:
MarketplaceDisplayType
.
SCORES
,
icon
:
'
apps_list
'
,
onlyIcon
:
false
,
contentAfter
:
(
<
IconSvg
name=
{
isMobile
?
'
beta_xs
'
:
'
beta
'
}
h=
{
3
}
w=
{
isMobile
?
3
:
7
}
ml=
{
1
}
/>
),
},
]
}
autoWidth
/
>
</
Skeleton
>
)
}
<
FilterInput
initialValue=
{
filterQuery
}
onChange=
{
onSearchInputChange
}
...
...
@@ -229,28 +193,16 @@ const Marketplace = () => {
/>
</
Flex
>
{
(
selectedDisplayType
===
MarketplaceDisplayType
.
SCORES
&&
feature
.
securityReportsUrl
)
?
(
<
MarketplaceListWithScores
apps=
{
displayedApps
}
showAppInfo=
{
showAppInfo
}
favoriteApps=
{
favoriteApps
}
onFavoriteClick=
{
onFavoriteClick
}
isLoading=
{
isPlaceholderData
}
selectedCategoryId=
{
selectedCategoryId
}
onAppClick=
{
handleAppClick
}
showContractList=
{
showContractList
}
/>
)
:
(
<
MarketplaceList
apps=
{
displayedApps
}
showAppInfo=
{
showAppInfo
}
favoriteApps=
{
favoriteApps
}
onFavoriteClick=
{
onFavoriteClick
}
isLoading=
{
isPlaceholderData
}
selectedCategoryId=
{
selectedCategoryId
}
onAppClick=
{
handleAppClick
}
/>
)
}
<
MarketplaceList
apps=
{
displayedApps
}
showAppInfo=
{
showAppInfo
}
favoriteApps=
{
favoriteApps
}
onFavoriteClick=
{
onFavoriteClick
}
isLoading=
{
isPlaceholderData
}
selectedCategoryId=
{
selectedCategoryId
}
onAppClick=
{
handleAppClick
}
showContractList=
{
showContractList
}
/>
{
(
selectedApp
&&
isAppInfoModalOpen
)
&&
(
<
MarketplaceAppModal
...
...
ui/shared/solidityscanReport/SolidityscanReportButton.tsx
View file @
bde9906e
import
{
Button
,
Spinner
,
Tooltip
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
{
Button
,
Spinner
,
Tooltip
,
useColorModeValue
,
chakra
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
...
...
@@ -13,10 +13,11 @@ interface Props {
onClick
?:
()
=>
void
;
label
?:
string
;
isActive
:
boolean
;
className
?:
string
;
}
const
SolidityscanReportButton
=
(
{
score
,
isLoading
,
onlyIcon
,
onClick
,
label
=
'
Security score
'
,
isActive
}:
Props
,
{
score
,
isLoading
,
onlyIcon
,
onClick
,
label
=
'
Security score
'
,
isActive
,
className
}:
Props
,
ref
:
React
.
ForwardedRef
<
HTMLButtonElement
>
,
)
=>
{
const
{
scoreColor
}
=
useScoreLevelAndColor
(
score
);
...
...
@@ -26,6 +27,7 @@ const SolidityscanReportButton = (
return
(
<
Tooltip
label=
{
label
}
isDisabled=
{
isMobile
}
openDelay=
{
100
}
>
<
Button
className=
{
className
}
ref=
{
ref
}
color=
{
isLoading
?
colorLoading
:
scoreColor
}
size=
"sm"
...
...
@@ -54,4 +56,4 @@ const SolidityscanReportButton = (
);
};
export
default
React
.
forwardRef
(
SolidityscanReportButton
);
export
default
chakra
(
React
.
forwardRef
(
SolidityscanReportButton
)
);
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