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
8b68dfdc
Unverified
Commit
8b68dfdc
authored
Sep 05, 2022
by
tom goriunov
Committed by
GitHub
Sep 05, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'main' into switch-fix
parents
3d44ca80
349f2e75
Changes
55
Show whitespace changes
Inline
Side-by-side
Showing
55 changed files
with
922 additions
and
309 deletions
+922
-309
key.svg
icons/key.svg
+3
-0
transactions.svg
icons/transactions.svg
+3
-1
Alert.ts
theme/components/Alert.ts
+23
-0
Form.ts
theme/components/Form.ts
+3
-0
Modal.ts
theme/components/Modal.ts
+19
-1
Textarea.ts
theme/components/Textarea.ts
+6
-0
ApiKeyListItem.tsx
ui/apiKey/ApiKeyTable/ApiKeyListItem.tsx
+33
-0
ApiKeyTableItem.tsx
ui/apiKey/ApiKeyTable/ApiKeyTableItem.tsx
+4
-14
DeleteApiKeyModal.tsx
ui/apiKey/DeleteApiKeyModal.tsx
+1
-1
Header.tsx
ui/blocks/header/Header.tsx
+1
-1
SearchBarMobile.tsx
ui/blocks/searchBar/SearchBarMobile.tsx
+1
-0
CustomAbiForm.tsx
ui/customAbi/CustomAbiModal/CustomAbiForm.tsx
+1
-0
CustomAbiListItem.tsx
ui/customAbi/CustomAbiTable/CustomAbiListItem.tsx
+33
-0
CustomAbiTableItem.tsx
ui/customAbi/CustomAbiTable/CustomAbiTableItem.tsx
+4
-14
DeleteCustomAbiModal.tsx
ui/customAbi/DeleteCustomAbiModal.tsx
+1
-1
ApiKeys.tsx
ui/pages/ApiKeys.tsx
+40
-16
CustomAbi.tsx
ui/pages/CustomAbi.tsx
+36
-12
PrivateTags.tsx
ui/pages/PrivateTags.tsx
+1
-1
PublicTags.tsx
ui/pages/PublicTags.tsx
+11
-1
Watchlist.tsx
ui/pages/Watchlist.tsx
+36
-11
AddressTagListItem.tsx
ui/privateTags/AddressTagTable/AddressTagListItem.tsx
+41
-0
AddressTagTableItem.tsx
ui/privateTags/AddressTagTable/AddressTagTableItem.tsx
+4
-13
DeletePrivateTagModal.tsx
ui/privateTags/DeletePrivateTagModal.tsx
+1
-1
PrivateAddressTags.tsx
ui/privateTags/PrivateAddressTags.tsx
+37
-13
PrivateTransactionTags.tsx
ui/privateTags/PrivateTransactionTags.tsx
+36
-12
TransactionTagListItem.tsx
...rivateTags/TransactionTagTable/TransactionTagListItem.tsx
+41
-0
TransactionTagTableItem.tsx
...ivateTags/TransactionTagTable/TransactionTagTableItem.tsx
+6
-11
DeletePublicTagModal.tsx
ui/publicTags/DeletePublicTagModal.tsx
+13
-12
PublicTagListItem.tsx
ui/publicTags/PublicTagTable/PublicTagListItem.tsx
+56
-0
PublicTagTableItem.tsx
ui/publicTags/PublicTagTable/PublicTagTableItem.tsx
+4
-22
PublicTagsData.tsx
ui/publicTags/PublicTagsData.tsx
+32
-6
PublicTagFormAddressInput.tsx
ui/publicTags/PublicTagsForm/PublicTagFormAddressInput.tsx
+35
-31
PublicTagFormComment.tsx
ui/publicTags/PublicTagsForm/PublicTagFormComment.tsx
+5
-4
PublicTagsForm.tsx
ui/publicTags/PublicTagsForm/PublicTagsForm.tsx
+16
-6
PublicTagsFormInput.tsx
ui/publicTags/PublicTagsForm/PublicTagsFormInput.tsx
+5
-3
AccountListItemMobile.tsx
ui/shared/AccountListItemMobile.tsx
+25
-0
AccountPageDescription.tsx
ui/shared/AccountPageDescription.tsx
+12
-0
AccountPageHeader.tsx
ui/shared/AccountPageHeader.tsx
+3
-3
AddressIcon.tsx
ui/shared/AddressIcon.tsx
+1
-1
AddressLinkWithTooltip.tsx
ui/shared/AddressLinkWithTooltip.tsx
+14
-3
AddressSnippet.tsx
ui/shared/AddressSnippet.tsx
+24
-0
ApiKeySnippet.tsx
ui/shared/ApiKeySnippet.tsx
+27
-0
DeleteButton.tsx
ui/shared/DeleteButton.tsx
+0
-27
DeleteModal.tsx
ui/shared/DeleteModal.tsx
+1
-1
EditButton.tsx
ui/shared/EditButton.tsx
+0
-27
FormModal.tsx
ui/shared/FormModal.tsx
+2
-2
SkeletonAccountMobile.tsx
ui/shared/SkeletonAccountMobile.tsx
+48
-0
TableItemActionButtons.tsx
ui/shared/TableItemActionButtons.tsx
+44
-0
TransactionSnippet.tsx
ui/shared/TransactionSnippet.tsx
+22
-0
AddressForm.tsx
ui/watchlist/AddressModal/AddressForm.tsx
+2
-2
AddressFormNotifications.tsx
ui/watchlist/AddressModal/AddressFormNotifications.tsx
+10
-2
DeleteAddressModal.tsx
ui/watchlist/DeleteAddressModal.tsx
+5
-2
WatchListAddressItem.tsx
ui/watchlist/WatchlistTable/WatchListAddressItem.tsx
+21
-24
WatchListItem.tsx
ui/watchlist/WatchlistTable/WatchListItem.tsx
+67
-0
WatchListTableItem.tsx
ui/watchlist/WatchlistTable/WatchListTableItem.tsx
+2
-7
No files found.
icons/key.svg
0 → 100644
View file @
8b68dfdc
<svg
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 24 24"
>
<path
fill-rule=
"evenodd"
clip-rule=
"evenodd"
d=
"M8 9a7 7 0 1 1 7 7h-2v2h-2v2H9v2H2v-5.414l6.148-6.148A7.025 7.025 0 0 1 8 9Zm3 5h4a5 5 0 1 0-4.786-3.547l.174.573L4 17.414V20h3v-2h2v-2h2v-2Zm4-3a2 2 0 1 1 0-4 2 2 0 0 1 0 4Z"
fill=
"currentColor"
/>
</svg>
\ No newline at end of file
icons/transactions.svg
View file @
8b68dfdc
<svg
width=
"30"
height=
"30"
fill=
"currentColor"
xmlns=
"http://www.w3.org/2000/svg"
><path
fill-rule=
"evenodd"
clip-rule=
"evenodd"
d=
"M23.433 12.64a1.212 1.212 0 0 1-.857.354H6.212a1.212 1.212 0 0 1-1.12-.745 1.212 1.212 0 0 1 .266-1.321L8.994 7.29a1.212 1.212 0 0 1 1.71 1.71L9.14 10.57h13.436a1.212 1.212 0 0 1 .857 2.068ZM7.424 17.236h16.363a1.213 1.213 0 0 1 1.121.745 1.212 1.212 0 0 1-.266 1.321l-3.636 3.637a1.213 1.213 0 1 1-1.71-1.71l1.564-1.569H7.424a1.212 1.212 0 0 1 0-2.424Z"
/></svg>
\ No newline at end of file
<svg
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 30 30"
>
<path
fill-rule=
"evenodd"
clip-rule=
"evenodd"
d=
"M23.433 12.64a1.212 1.212 0 0 1-.857.354H6.212a1.212 1.212 0 0 1-1.12-.745 1.212 1.212 0 0 1 .266-1.321L8.994 7.29a1.212 1.212 0 0 1 1.71 1.71L9.14 10.57h13.436a1.212 1.212 0 0 1 .857 2.068ZM7.424 17.236h16.363a1.213 1.213 0 0 1 1.121.745 1.212 1.212 0 0 1-.266 1.321l-3.636 3.637a1.213 1.213 0 1 1-1.71-1.71l1.564-1.569H7.424a1.212 1.212 0 0 1 0-2.424Z"
fill=
"currentColor"
/>
</svg>
\ No newline at end of file
theme/components/Alert.ts
View file @
8b68dfdc
import
{
alertAnatomy
as
parts
}
from
'
@chakra-ui/anatomy
'
;
import
type
{
StyleFunctionProps
}
from
'
@chakra-ui/styled-system
'
;
import
{
createMultiStyleConfigHelpers
}
from
'
@chakra-ui/styled-system
'
;
import
{
getColor
,
mode
,
transparentize
}
from
'
@chakra-ui/theme-tools
'
;
const
{
definePartsStyle
,
defineMultiStyleConfig
}
=
createMultiStyleConfigHelpers
(
parts
.
keys
);
function
getBg
(
props
:
StyleFunctionProps
):
string
{
const
{
theme
,
colorScheme
:
c
}
=
props
;
const
lightBg
=
getColor
(
theme
,
`
${
c
}
.100`
,
c
);
const
darkBg
=
transparentize
(
`
${
c
}
.200`
,
0.16
)(
theme
);
return
mode
(
lightBg
,
darkBg
)(
props
);
}
const
baseStyle
=
definePartsStyle
({
container
:
{
borderRadius
:
'
md
'
,
...
...
@@ -12,8 +22,21 @@ const baseStyle = definePartsStyle({
},
});
const
variantSubtle
=
definePartsStyle
((
props
)
=>
{
return
{
container
:
{
bgColor
:
getBg
(
props
),
},
};
});
const
variants
=
{
subtle
:
variantSubtle
,
};
const
Alert
=
defineMultiStyleConfig
({
baseStyle
,
variants
,
});
export
default
Alert
;
theme/components/Form.ts
View file @
8b68dfdc
...
...
@@ -125,6 +125,9 @@ const variantFloating = definePartsStyle((props) => {
margin
:
0
,
transformOrigin
:
'
top left
'
,
transitionProperty
:
'
font-size, line-height, padding, top, background-color
'
,
overflow
:
'
hidden
'
,
whiteSpace
:
'
nowrap
'
,
textOverflow
:
'
ellipsis
'
,
},
'
input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label
'
:
{
...
activeLabelStyles
,
...
...
theme/components/Modal.ts
View file @
8b68dfdc
...
...
@@ -35,6 +35,7 @@ const baseStyleHeader = defineStyle((props) => ({
const
baseStyleBody
=
defineStyle
({
padding
:
0
,
marginBottom
:
8
,
flex
:
'
initial
'
,
});
const
baseStyleFooter
=
defineStyle
({
...
...
@@ -70,16 +71,33 @@ const baseStyle = definePartsStyle((props) => ({
const
sizes
=
{
md
:
definePartsStyle
({
dialogContainer
:
{
height
:
'
100%
'
,
},
dialog
:
{
maxW
:
'
760px
'
,
},
}),
full
:
definePartsStyle
({
dialogContainer
:
{
height
:
'
100%
'
,
},
dialog
:
{
maxW
:
'
100vw
'
,
minH
:
'
100vh
'
,
my
:
'
0
'
,
borderRadius
:
'
0
'
,
padding
:
'
80px 16px 32px 16px
'
,
height
:
'
100%
'
,
overflowY
:
'
scroll
'
,
},
closeButton
:
{
top
:
4
,
right
:
6
,
width
:
6
,
height
:
6
,
},
header
:
{
mb
:
6
,
},
}),
};
...
...
theme/components/Textarea.ts
View file @
8b68dfdc
...
...
@@ -4,6 +4,12 @@ import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
import
getOutlinedFieldStyles
from
'
../utils/getOutlinedFieldStyles
'
;
const
sizes
=
{
md
:
defineStyle
({
fontSize
:
'
md
'
,
lineHeight
:
'
20px
'
,
h
:
'
160px
'
,
borderRadius
:
'
base
'
,
}),
lg
:
defineStyle
({
fontSize
:
'
md
'
,
lineHeight
:
'
20px
'
,
...
...
ui/apiKey/ApiKeyTable/ApiKeyListItem.tsx
0 → 100644
View file @
8b68dfdc
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
ApiKey
}
from
'
types/api/account
'
;
import
AccountListItemMobile
from
'
ui/shared/AccountListItemMobile
'
;
import
ApiKeySnippet
from
'
ui/shared/ApiKeySnippet
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
interface
Props
{
item
:
ApiKey
;
onEditClick
:
(
item
:
ApiKey
)
=>
void
;
onDeleteClick
:
(
item
:
ApiKey
)
=>
void
;
}
const
ApiKeyListItem
=
({
item
,
onEditClick
,
onDeleteClick
}:
Props
)
=>
{
const
onItemEditClick
=
useCallback
(()
=>
{
return
onEditClick
(
item
);
},
[
item
,
onEditClick
]);
const
onItemDeleteClick
=
useCallback
(()
=>
{
return
onDeleteClick
(
item
);
},
[
item
,
onDeleteClick
]);
return
(
<
AccountListItemMobile
>
<
ApiKeySnippet
apiKey=
{
item
.
api_key
}
name=
{
item
.
name
}
/>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
AccountListItemMobile
>
);
};
export
default
ApiKeyListItem
;
ui/apiKey/ApiKeyTable/ApiKeyTableItem.tsx
View file @
8b68dfdc
import
{
Tr
,
Td
,
HStack
,
Text
,
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
ApiKey
}
from
'
types/api/account
'
;
import
CopyToClipboard
from
'
ui/shared/CopyToClipboard
'
;
import
DeleteButton
from
'
ui/shared/DeleteButton
'
;
import
EditButton
from
'
ui/shared/EditButton
'
;
import
ApiKeySnippet
from
'
ui/shared/ApiKeySnippet
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
interface
Props
{
item
:
ApiKey
;
...
...
@@ -31,17 +28,10 @@ const ApiKeyTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
return
(
<
Tr
alignItems=
"top"
key=
{
item
.
api_key
}
>
<
Td
>
<
HStack
>
<
Text
fontSize=
"md"
fontWeight=
{
600
}
>
{
item
.
api_key
}
</
Text
>
<
CopyToClipboard
text=
{
item
.
api_key
}
/>
</
HStack
>
<
Text
fontSize=
"sm"
marginTop=
{
0.5
}
variant=
"secondary"
>
{
item
.
name
}
</
Text
>
<
ApiKeySnippet
apiKey=
{
item
.
api_key
}
name=
{
item
.
name
}
/>
</
Td
>
<
Td
>
<
HStack
spacing=
{
6
}
>
<
EditButton
onClick=
{
onItemEditClick
}
/>
<
DeleteButton
onClick=
{
onItemDeleteClick
}
/>
</
HStack
>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
Td
>
</
Tr
>
);
...
...
ui/apiKey/DeleteApiKeyModal.tsx
View file @
8b68dfdc
...
...
@@ -28,7 +28,7 @@ const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
const
renderText
=
useCallback
(()
=>
{
return
(
<
Text
display=
"flex"
>
API key for
<
Text
fontWeight=
"600"
whiteSpace=
"pre"
>
{
` "${ data.name || 'name' }" `
}
</
Text
>
will be deleted
</
Text
>
<
Text
>
API key for
<
Text
fontWeight=
"600"
as=
"span"
>
{
` "${ data.name || 'name' }" `
}
</
Text
>
will be deleted
</
Text
>
);
},
[
data
.
name
]);
...
...
ui/blocks/header/Header.tsx
View file @
8b68dfdc
...
...
@@ -28,7 +28,7 @@ const Header = () => {
width=
"100%"
alignItems=
"center"
justifyContent=
"space-between"
zIndex=
{
10
}
zIndex=
"sticky"
>
<
Burger
/>
<
NetworkLogo
/>
...
...
ui/blocks/searchBar/SearchBarMobile.tsx
View file @
8b68dfdc
...
...
@@ -56,6 +56,7 @@ const SearchBarMobile = ({ onChange, onSubmit }: Props) => {
position=
"fixed"
top=
"56px"
left=
"0"
zIndex=
"docked"
bgColor=
{
bgColor
}
transform=
{
isVisible
?
'
translateY(0)
'
:
'
translateY(-100%)
'
}
transitionProperty=
"transform"
...
...
ui/customAbi/CustomAbiModal/CustomAbiForm.tsx
View file @
8b68dfdc
...
...
@@ -128,6 +128,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
<
Textarea
{
...
field
}
size=
"lg"
minH=
"300px"
isInvalid=
{
Boolean
(
errors
.
abi
)
}
/>
<
FormLabel
>
{
getPlaceholderWithError
(
`Custom ABI [{...}] (JSON format)`
,
errors
.
abi
?.
message
)
}
</
FormLabel
>
...
...
ui/customAbi/CustomAbiTable/CustomAbiListItem.tsx
0 → 100644
View file @
8b68dfdc
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
CustomAbi
}
from
'
types/api/account
'
;
import
AccountListItemMobile
from
'
ui/shared/AccountListItemMobile
'
;
import
AddressSnippet
from
'
ui/shared/AddressSnippet
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
interface
Props
{
item
:
CustomAbi
;
onEditClick
:
(
item
:
CustomAbi
)
=>
void
;
onDeleteClick
:
(
item
:
CustomAbi
)
=>
void
;
}
const
CustomAbiListItem
=
({
item
,
onEditClick
,
onDeleteClick
}:
Props
)
=>
{
const
onItemEditClick
=
useCallback
(()
=>
{
return
onEditClick
(
item
);
},
[
item
,
onEditClick
]);
const
onItemDeleteClick
=
useCallback
(()
=>
{
return
onDeleteClick
(
item
);
},
[
item
,
onDeleteClick
]);
return
(
<
AccountListItemMobile
>
<
AddressSnippet
address=
{
item
.
contract_address_hash
}
subtitle=
{
item
.
name
}
/>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
AccountListItemMobile
>
);
};
export
default
React
.
memo
(
CustomAbiListItem
);
ui/customAbi/CustomAbiTable/CustomAbiTableItem.tsx
View file @
8b68dfdc
import
{
Tr
,
Td
,
HStack
,
Text
,
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
CustomAbi
}
from
'
types/api/account
'
;
import
CopyToClipboard
from
'
ui/shared/CopyToClipboard
'
;
import
DeleteButton
from
'
ui/shared/DeleteButton
'
;
import
EditButton
from
'
ui/shared/EditButton
'
;
import
AddressSnippet
from
'
ui/shared/AddressSnippet
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
interface
Props
{
item
:
CustomAbi
;
...
...
@@ -31,17 +28,10 @@ const CustomAbiTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
return
(
<
Tr
alignItems=
"top"
key=
{
item
.
id
}
>
<
Td
>
<
HStack
>
<
Text
fontSize=
"md"
fontWeight=
{
600
}
>
{
item
.
contract_address_hash
}
</
Text
>
<
CopyToClipboard
text=
{
item
.
contract_address_hash
}
/>
</
HStack
>
<
Text
fontSize=
"sm"
marginTop=
{
0.5
}
variant=
"secondary"
>
{
item
.
name
}
</
Text
>
<
AddressSnippet
address=
{
item
.
contract_address_hash
}
subtitle=
{
item
.
name
}
/>
</
Td
>
<
Td
>
<
HStack
spacing=
{
6
}
>
<
EditButton
onClick=
{
onItemEditClick
}
/>
<
DeleteButton
onClick=
{
onItemDeleteClick
}
/>
</
HStack
>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
Td
>
</
Tr
>
);
...
...
ui/customAbi/DeleteCustomAbiModal.tsx
View file @
8b68dfdc
...
...
@@ -28,7 +28,7 @@ const DeleteCustomAbiModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
const
renderText
=
useCallback
(()
=>
{
return
(
<
Text
display=
"flex"
>
Custom ABI for
<
Text
fontWeight=
"600"
whiteSpace=
"pre
"
>
{
` "${ data.name || 'name' }" `
}
</
Text
>
will be deleted
</
Text
>
<
Text
>
Custom ABI for
<
Text
fontWeight=
"600"
as=
"span
"
>
{
` "${ data.name || 'name' }" `
}
</
Text
>
will be deleted
</
Text
>
);
},
[
data
.
name
]);
...
...
ui/pages/ApiKeys.tsx
View file @
8b68dfdc
import
{
Box
,
Button
,
H
Stack
,
Link
,
Text
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Button
,
Stack
,
Link
,
Text
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
React
,
{
useCallback
,
useState
}
from
'
react
'
;
import
type
{
ApiKey
,
ApiKeys
}
from
'
types/api/account
'
;
import
fetch
from
'
lib/client/fetch
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
{
space
}
from
'
lib/html-entities
'
;
import
ApiKeyModal
from
'
ui/apiKey/ApiKeyModal/ApiKeyModal
'
;
import
ApiKeyListItem
from
'
ui/apiKey/ApiKeyTable/ApiKeyListItem
'
;
import
ApiKeyTable
from
'
ui/apiKey/ApiKeyTable/ApiKeyTable
'
;
import
DeleteApiKeyModal
from
'
ui/apiKey/DeleteApiKeyModal
'
;
import
AccountPageDescription
from
'
ui/shared/AccountPageDescription
'
;
import
AccountPageHeader
from
'
ui/shared/AccountPageHeader
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
import
SkeletonAccountMobile
from
'
ui/shared/SkeletonAccountMobile
'
;
import
SkeletonTable
from
'
ui/shared/SkeletonTable
'
;
import
DataFetchAlert
from
'
../shared/DataFetchAlert
'
;
...
...
@@ -20,6 +24,7 @@ const DATA_LIMIT = 3;
const
ApiKeysPage
:
React
.
FC
=
()
=>
{
const
apiKeyModalProps
=
useDisclosure
();
const
deleteModalProps
=
useDisclosure
();
const
isMobile
=
useIsMobile
();
const
[
apiKeyModalData
,
setApiKeyModalData
]
=
useState
<
ApiKey
>
();
const
[
deleteModalData
,
setDeleteModalData
]
=
useState
<
ApiKey
>
();
...
...
@@ -47,40 +52,59 @@ const ApiKeysPage: React.FC = () => {
},
[
deleteModalProps
]);
const
description
=
(
<
Text
marginBottom=
{
12
}
>
<
AccountPageDescription
>
Create API keys to use for your RPC and EthRPC API requests. For more information, see
{
space
}
<
Link
href=
"#"
>
“How to use a Blockscout API key”
</
Link
>
.
</
Text
>
</
AccountPageDescription
>
);
const
content
=
(()
=>
{
if
(
isLoading
&&
!
data
)
{
return
(
const
loader
=
isMobile
?
<
SkeletonAccountMobile
/>
:
(
<>
{
description
}
<
SkeletonTable
columns=
{
[
'
100%
'
,
'
108px
'
]
}
/>
<
Skeleton
height=
"48px"
width=
"156px"
marginTop=
{
8
}
/>
</>
);
return
(
<>
{
description
}
{
loader
}
</>
);
}
if
(
isError
)
{
return
<
DataFetchAlert
/>;
}
const
canAdd
=
data
.
length
<
DATA_LIMIT
;
return
(
<>
{
description
}
{
Boolean
(
data
.
length
)
&&
(
const
list
=
isMobile
?
(
<
Box
>
{
data
.
map
((
item
)
=>
(
<
ApiKeyListItem
item=
{
item
}
key=
{
item
.
api_key
}
onDeleteClick=
{
onDeleteClick
}
onEditClick=
{
onEditClick
}
/>
))
}
</
Box
>
)
:
(
<
ApiKeyTable
data=
{
data
}
onDeleteClick=
{
onDeleteClick
}
onEditClick=
{
onEditClick
}
limit=
{
DATA_LIMIT
}
/>
)
}
<
HStack
marginTop=
{
8
}
spacing=
{
5
}
>
);
const
canAdd
=
data
.
length
<
DATA_LIMIT
;
return
(
<>
{
description
}
{
Boolean
(
data
.
length
)
&&
list
}
<
Stack
marginTop=
{
8
}
spacing=
{
5
}
direction=
{
{
base
:
'
column
'
,
lg
:
'
row
'
}
}
>
<
Button
variant=
"primary"
size=
"lg"
...
...
@@ -94,7 +118,7 @@ const ApiKeysPage: React.FC = () => {
{
`You have added the maximum number of API keys (${ DATA_LIMIT }). Contact us to request additional keys.`
}
</
Text
>
)
}
</
H
Stack
>
</
Stack
>
<
ApiKeyModal
{
...
apiKeyModalProps
}
onClose=
{
onApiKeyModalClose
}
data=
{
apiKeyModalData
}
/>
{
deleteModalData
&&
<
DeleteApiKeyModal
{
...
deleteModalProps
}
onClose=
{
onDeleteModalClose
}
data=
{
deleteModalData
}
/>
}
</>
...
...
ui/pages/CustomAbi.tsx
View file @
8b68dfdc
import
{
Box
,
Button
,
HStack
,
Text
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Button
,
HStack
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
React
,
{
useCallback
,
useState
}
from
'
react
'
;
import
type
{
CustomAbi
,
CustomAbis
}
from
'
types/api/account
'
;
import
fetch
from
'
lib/client/fetch
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
CustomAbiModal
from
'
ui/customAbi/CustomAbiModal/CustomAbiModal
'
;
import
CustomAbiListItem
from
'
ui/customAbi/CustomAbiTable/CustomAbiListItem
'
;
import
CustomAbiTable
from
'
ui/customAbi/CustomAbiTable/CustomAbiTable
'
;
import
DeleteCustomAbiModal
from
'
ui/customAbi/DeleteCustomAbiModal
'
;
import
AccountPageDescription
from
'
ui/shared/AccountPageDescription
'
;
import
AccountPageHeader
from
'
ui/shared/AccountPageHeader
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
import
SkeletonAccountMobile
from
'
ui/shared/SkeletonAccountMobile
'
;
import
SkeletonTable
from
'
ui/shared/SkeletonTable
'
;
import
DataFetchAlert
from
'
../shared/DataFetchAlert
'
;
...
...
@@ -17,6 +21,7 @@ import DataFetchAlert from '../shared/DataFetchAlert';
const
CustomAbiPage
:
React
.
FC
=
()
=>
{
const
customAbiModalProps
=
useDisclosure
();
const
deleteModalProps
=
useDisclosure
();
const
isMobile
=
useIsMobile
();
const
[
customAbiModalData
,
setCustomAbiModalData
]
=
useState
<
CustomAbi
>
();
const
[
deleteModalData
,
setDeleteModalData
]
=
useState
<
CustomAbi
>
();
...
...
@@ -44,36 +49,55 @@ const CustomAbiPage: React.FC = () => {
},
[
deleteModalProps
]);
const
description
=
(
<
Text
marginBottom=
{
12
}
>
<
AccountPageDescription
>
Add custom ABIs for any contract and access when logged into your account. Helpful for debugging, functional testing and contract interaction.
</
Text
>
</
AccountPageDescription
>
);
const
content
=
(()
=>
{
if
(
isLoading
&&
!
data
)
{
return
(
const
loader
=
isMobile
?
<
SkeletonAccountMobile
/>
:
(
<>
{
description
}
<
SkeletonTable
columns=
{
[
'
100%
'
,
'
108px
'
]
}
/>
<
Skeleton
height=
"44px"
width=
"156px"
marginTop=
{
8
}
/>
</>
);
return
(
<>
{
description
}
{
loader
}
</>
);
}
if
(
isError
)
{
return
<
DataFetchAlert
/>;
}
return
(
<>
{
description
}
{
data
.
length
>
0
&&
(
const
list
=
isMobile
?
(
<
Box
>
{
data
.
map
((
item
)
=>
(
<
CustomAbiListItem
item=
{
item
}
key=
{
item
.
id
}
onDeleteClick=
{
onDeleteClick
}
onEditClick=
{
onEditClick
}
/>
))
}
</
Box
>
)
:
(
<
CustomAbiTable
data=
{
data
}
onDeleteClick=
{
onDeleteClick
}
onEditClick=
{
onEditClick
}
/>
)
}
);
return
(
<>
{
description
}
{
data
.
length
>
0
&&
list
}
<
HStack
marginTop=
{
8
}
spacing=
{
5
}
>
<
Button
variant=
"primary"
...
...
ui/pages/PrivateTags.tsx
View file @
8b68dfdc
...
...
@@ -38,7 +38,7 @@ const PrivateTags = ({ tab }: Props) => {
<
Box
h=
"100%"
>
<
AccountPageHeader
text=
"Private tags"
/>
<
Tabs
variant=
"soft-rounded"
colorScheme=
"blue"
isLazy
onChange=
{
onChangeTab
}
defaultIndex=
{
TABS
.
indexOf
(
tab
)
}
>
<
TabList
marginBottom=
{
8
}
>
<
TabList
marginBottom=
{
{
base
:
6
,
lg
:
8
}
}
>
<
Tab
>
Address
</
Tab
>
<
Tab
>
Transaction
</
Tab
>
</
TabList
>
...
...
ui/pages/PublicTags.tsx
View file @
8b68dfdc
import
{
Box
}
from
'
@chakra-ui/react
'
;
import
{
ArrowBackIcon
}
from
'
@chakra-ui/icons
'
;
import
{
Box
,
Link
,
Text
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
,
useState
}
from
'
react
'
;
import
{
animateScroll
}
from
'
react-scroll
'
;
import
type
{
PublicTag
}
from
'
types/api/account
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
useToast
from
'
lib/hooks/useToast
'
;
import
PublicTagsData
from
'
ui/publicTags/PublicTagsData
'
;
import
PublicTagsForm
from
'
ui/publicTags/PublicTagsForm/PublicTagsForm
'
;
...
...
@@ -24,6 +26,7 @@ const PublicTagsComponent: React.FC = () => {
const
[
formData
,
setFormData
]
=
useState
<
PublicTag
>
();
const
toast
=
useToast
();
const
isMobile
=
useIsMobile
();
const
showToast
=
useCallback
((
action
:
TToastAction
)
=>
{
toast
({
...
...
@@ -59,6 +62,7 @@ const PublicTagsComponent: React.FC = () => {
},
[
showToast
]);
const
onTagDelete
=
useCallback
(()
=>
showToast
(
'
removed
'
),
[
showToast
]);
const
onGoBack
=
useCallback
(()
=>
setScreen
(
'
data
'
),
[
]);
let
content
;
let
header
;
...
...
@@ -74,6 +78,12 @@ const PublicTagsComponent: React.FC = () => {
return
(
<
Page
>
<
Box
h=
"100%"
>
{
isMobile
&&
screen
===
'
form
'
&&
(
<
Link
display=
"inline-flex"
alignItems=
"center"
mb=
{
6
}
onClick=
{
onGoBack
}
>
<
ArrowBackIcon
w=
{
6
}
h=
{
6
}
/>
<
Text
variant=
"inherit"
fontSize=
"sm"
ml=
{
2
}
>
Public tags
</
Text
>
</
Link
>
)
}
<
AccountPageHeader
text=
{
header
}
/>
{
content
}
</
Box
>
...
...
ui/pages/Watchlist.tsx
View file @
8b68dfdc
import
{
Box
,
Button
,
Text
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Button
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
React
,
{
useCallback
,
useState
}
from
'
react
'
;
import
type
{
TWatchlist
,
TWatchlistItem
}
from
'
types/client/account
'
;
import
fetch
from
'
lib/client/fetch
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
AccountPageDescription
from
'
ui/shared/AccountPageDescription
'
;
import
AccountPageHeader
from
'
ui/shared/AccountPageHeader
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
import
SkeletonAccountMobile
from
'
ui/shared/SkeletonAccountMobile
'
;
import
SkeletonTable
from
'
ui/shared/SkeletonTable
'
;
import
AddressModal
from
'
ui/watchlist/AddressModal/AddressModal
'
;
import
DeleteAddressModal
from
'
ui/watchlist/DeleteAddressModal
'
;
import
WatchListItem
from
'
ui/watchlist/WatchlistTable/WatchListItem
'
;
import
WatchlistTable
from
'
ui/watchlist/WatchlistTable/WatchlistTable
'
;
const
WatchList
:
React
.
FC
=
()
=>
{
...
...
@@ -19,6 +23,7 @@ const WatchList: React.FC = () => {
const
addressModalProps
=
useDisclosure
();
const
deleteModalProps
=
useDisclosure
();
const
isMobile
=
useIsMobile
();
const
[
addressModalData
,
setAddressModalData
]
=
useState
<
TWatchlistItem
>
();
const
[
deleteModalData
,
setDeleteModalData
]
=
useState
<
TWatchlistItem
>
();
...
...
@@ -44,32 +49,52 @@ const WatchList: React.FC = () => {
},
[
deleteModalProps
]);
const
description
=
(
<
Text
marginBottom=
{
12
}
>
<
AccountPageDescription
>
An email notification can be sent to you when an address on your watch list sends or receives any transactions.
</
Text
>
</
AccountPageDescription
>
);
let
content
;
if
(
isLoading
&&
!
data
)
{
con
tent
=
(
con
st
loader
=
isMobile
?
<
SkeletonAccountMobile
showFooterSlot
/>
:
(
<>
{
description
}
<
SkeletonTable
columns=
{
[
'
70%
'
,
'
30%
'
,
'
160px
'
,
'
108px
'
]
}
/>
<
Skeleton
height=
"44px"
width=
"156px"
marginTop=
{
8
}
/>
</>
);
content
=
(
<>
{
description
}
{
loader
}
</>
);
}
else
if
(
isError
)
{
content
=
<
DataFetchAlert
/>;
}
else
{
content
=
(
<>
{
Boolean
(
data
?.
length
)
&&
(
const
list
=
isMobile
?
(
<
Box
>
{
data
.
map
((
item
)
=>
(
<
WatchListItem
item=
{
item
}
key=
{
item
.
address_hash
}
onDeleteClick=
{
onDeleteClick
}
onEditClick=
{
onEditClick
}
/>
))
}
</
Box
>
)
:
(
<
WatchlistTable
data=
{
data
}
onDeleteClick=
{
onDeleteClick
}
onEditClick=
{
onEditClick
}
/>
)
}
);
content
=
(
<>
{
description
}
{
Boolean
(
data
?.
length
)
&&
list
}
<
Box
marginTop=
{
8
}
>
<
Button
variant=
"primary"
...
...
ui/privateTags/AddressTagTable/AddressTagListItem.tsx
0 → 100644
View file @
8b68dfdc
import
{
Tag
,
Flex
,
HStack
,
Text
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
AddressTag
}
from
'
types/api/account
'
;
import
AccountListItemMobile
from
'
ui/shared/AccountListItemMobile
'
;
import
AddressSnippet
from
'
ui/shared/AddressSnippet
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
interface
Props
{
item
:
AddressTag
;
onEditClick
:
(
data
:
AddressTag
)
=>
void
;
onDeleteClick
:
(
data
:
AddressTag
)
=>
void
;
}
const
AddressTagListItem
=
({
item
,
onEditClick
,
onDeleteClick
}:
Props
)
=>
{
const
onItemEditClick
=
useCallback
(()
=>
{
return
onEditClick
(
item
);
},
[
item
,
onEditClick
]);
const
onItemDeleteClick
=
useCallback
(()
=>
{
return
onDeleteClick
(
item
);
},
[
item
,
onDeleteClick
]);
return
(
<
AccountListItemMobile
>
<
Flex
alignItems=
"flex-start"
flexDirection=
"column"
maxW=
"100%"
>
<
AddressSnippet
address=
{
item
.
address_hash
}
/>
<
HStack
spacing=
{
3
}
mt=
{
4
}
>
<
Text
fontSize=
"sm"
fontWeight=
{
500
}
>
Private tag
</
Text
>
<
Tag
variant=
"gray"
lineHeight=
"24px"
>
{
item
.
name
}
</
Tag
>
</
HStack
>
</
Flex
>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
AccountListItemMobile
>
);
};
export
default
React
.
memo
(
AddressTagListItem
);
ui/privateTags/AddressTagTable/AddressTagTableItem.tsx
View file @
8b68dfdc
...
...
@@ -2,16 +2,13 @@ import {
Tag
,
Tr
,
Td
,
HStack
,
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
AddressTag
}
from
'
types/api/account
'
;
import
AddressIcon
from
'
ui/shared/AddressIcon
'
;
import
AddressLinkWithTooltip
from
'
ui/shared/AddressLinkWithTooltip
'
;
import
DeleteButton
from
'
ui/shared/DeleteButton
'
;
import
EditButton
from
'
ui/shared/EditButton
'
;
import
AddressSnippet
from
'
ui/shared/AddressSnippet
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
import
TruncatedTextTooltip
from
'
ui/shared/TruncatedTextTooltip
'
;
interface
Props
{
...
...
@@ -32,10 +29,7 @@ const AddressTagTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
return
(
<
Tr
alignItems=
"top"
key=
{
item
.
id
}
>
<
Td
>
<
HStack
spacing=
{
4
}
>
<
AddressIcon
address=
{
item
.
address_hash
}
/>
<
AddressLinkWithTooltip
address=
{
item
.
address_hash
}
/>
</
HStack
>
<
AddressSnippet
address=
{
item
.
address_hash
}
/>
</
Td
>
<
Td
>
<
TruncatedTextTooltip
label=
{
item
.
name
}
>
...
...
@@ -45,10 +39,7 @@ const AddressTagTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
</
TruncatedTextTooltip
>
</
Td
>
<
Td
>
<
HStack
spacing=
{
6
}
>
<
EditButton
onClick=
{
onItemEditClick
}
/>
<
DeleteButton
onClick=
{
onItemDeleteClick
}
/>
</
HStack
>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
Td
>
</
Tr
>
);
...
...
ui/privateTags/DeletePrivateTagModal.tsx
View file @
8b68dfdc
...
...
@@ -37,7 +37,7 @@ const DeletePrivateTagModal: React.FC<Props> = ({ isOpen, onClose, data, type })
const
renderText
=
useCallback
(()
=>
{
return
(
<
Text
display=
"flex"
>
Tag
<
Text
fontWeight=
"600"
whiteSpace=
"pre
"
>
{
` "${ tag || 'tag' }" `
}
</
Text
>
will be deleted
</
Text
>
<
Text
>
Tag
<
Text
fontWeight=
"600"
as=
"span
"
>
{
` "${ tag || 'tag' }" `
}
</
Text
>
will be deleted
</
Text
>
);
},
[
tag
]);
...
...
ui/privateTags/PrivateAddressTags.tsx
View file @
8b68dfdc
import
{
Box
,
Button
,
Text
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Button
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
React
,
{
useCallback
,
useState
}
from
'
react
'
;
import
type
{
AddressTags
,
AddressTag
}
from
'
types/api/account
'
;
import
fetch
from
'
lib/client/fetch
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
AccountPageDescription
from
'
ui/shared/AccountPageDescription
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
SkeletonAccountMobile
from
'
ui/shared/SkeletonAccountMobile
'
;
import
SkeletonTable
from
'
ui/shared/SkeletonTable
'
;
import
AddressModal
from
'
./AddressModal/AddressModal
'
;
import
AddressTagListItem
from
'
./AddressTagTable/AddressTagListItem
'
;
import
AddressTagTable
from
'
./AddressTagTable/AddressTagTable
'
;
import
DeletePrivateTagModal
from
'
./DeletePrivateTagModal
'
;
...
...
@@ -18,6 +22,7 @@ const PrivateAddressTags = () => {
const
addressModalProps
=
useDisclosure
();
const
deleteModalProps
=
useDisclosure
();
const
isMobile
=
useIsMobile
();
const
[
addressModalData
,
setAddressModalData
]
=
useState
<
AddressTag
>
();
const
[
deleteModalData
,
setDeleteModalData
]
=
useState
<
AddressTag
>
();
...
...
@@ -43,36 +48,55 @@ const PrivateAddressTags = () => {
},
[
deleteModalProps
]);
const
description
=
(
<
Text
marginBottom=
{
12
}
>
Use private
transaction tags to label any transaction
s of interest.
<
AccountPageDescription
>
Use private
address tags to track any addresse
s of interest.
Private tags are saved in your account and are only visible when you are logged in.
</
Text
>
</
AccountPageDescription
>
);
if
(
isLoading
&&
!
addressTagsData
)
{
return
(
const
loader
=
isMobile
?
<
SkeletonAccountMobile
/>
:
(
<>
{
description
}
<
SkeletonTable
columns=
{
[
'
60%
'
,
'
40%
'
,
'
108px
'
]
}
/>
<
Skeleton
height=
"44px"
width=
"156px"
marginTop=
{
8
}
/>
</>
);
return
(
<>
{
description
}
{
loader
}
</>
);
}
if
(
isError
)
{
return
<
DataFetchAlert
/>;
}
return
(
<>
{
description
}
{
Boolean
(
addressTagsData
?.
length
)
&&
(
const
list
=
isMobile
?
(
<
Box
>
{
addressTagsData
.
map
((
item
:
AddressTag
)
=>
(
<
AddressTagListItem
item=
{
item
}
key=
{
item
.
id
}
onDeleteClick=
{
onDeleteClick
}
onEditClick=
{
onEditClick
}
/>
))
}
</
Box
>
)
:
(
<
AddressTagTable
data=
{
addressTagsData
}
onDeleteClick=
{
onDeleteClick
}
onEditClick=
{
onEditClick
}
/>
)
}
);
return
(
<>
{
description
}
{
Boolean
(
addressTagsData
?.
length
)
&&
list
}
<
Box
marginTop=
{
8
}
>
<
Button
variant=
"primary"
...
...
ui/privateTags/PrivateTransactionTags.tsx
View file @
8b68dfdc
import
{
Box
,
Button
,
Skeleton
,
Text
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Button
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
React
,
{
useCallback
,
useState
}
from
'
react
'
;
import
type
{
TransactionTags
,
TransactionTag
}
from
'
types/api/account
'
;
import
fetch
from
'
lib/client/fetch
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
AccountPageDescription
from
'
ui/shared/AccountPageDescription
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
SkeletonAccountMobile
from
'
ui/shared/SkeletonAccountMobile
'
;
import
SkeletonTable
from
'
ui/shared/SkeletonTable
'
;
import
DeletePrivateTagModal
from
'
./DeletePrivateTagModal
'
;
import
TransactionModal
from
'
./TransactionModal/TransactionModal
'
;
import
TransactionTagListItem
from
'
./TransactionTagTable/TransactionTagListItem
'
;
import
TransactionTagTable
from
'
./TransactionTagTable/TransactionTagTable
'
;
const
PrivateTransactionTags
=
()
=>
{
...
...
@@ -18,6 +22,7 @@ const PrivateTransactionTags = () => {
const
transactionModalProps
=
useDisclosure
();
const
deleteModalProps
=
useDisclosure
();
const
isMobile
=
useIsMobile
();
const
[
transactionModalData
,
setTransactionModalData
]
=
useState
<
TransactionTag
>
();
const
[
deleteModalData
,
setDeleteModalData
]
=
useState
<
TransactionTag
>
();
...
...
@@ -43,36 +48,55 @@ const PrivateTransactionTags = () => {
},
[
deleteModalProps
]);
const
description
=
(
<
Text
marginBottom=
{
12
}
>
<
AccountPageDescription
>
Use private transaction tags to label any transactions of interest.
Private tags are saved in your account and are only visible when you are logged in.
</
Text
>
</
AccountPageDescription
>
);
if
(
isLoading
&&
!
transactionTagsData
)
{
return
(
const
loader
=
isMobile
?
<
SkeletonAccountMobile
/>
:
(
<>
{
description
}
<
SkeletonTable
columns=
{
[
'
75%
'
,
'
25%
'
,
'
108px
'
]
}
/>
<
Skeleton
height=
"44px"
width=
"156px"
marginTop=
{
8
}
/>
</>
);
return
(
<>
{
description
}
{
loader
}
</>
);
}
if
(
isError
)
{
return
<
DataFetchAlert
/>;
}
return
(
<>
{
description
}
{
Boolean
(
transactionTagsData
.
length
)
&&
(
const
list
=
isMobile
?
(
<
Box
>
{
transactionTagsData
.
map
((
item
)
=>
(
<
TransactionTagListItem
item=
{
item
}
key=
{
item
.
id
}
onDeleteClick=
{
onDeleteClick
}
onEditClick=
{
onEditClick
}
/>
))
}
</
Box
>
)
:
(
<
TransactionTagTable
data=
{
transactionTagsData
}
onDeleteClick=
{
onDeleteClick
}
onEditClick=
{
onEditClick
}
/>
)
}
);
return
(
<>
{
description
}
{
Boolean
(
transactionTagsData
.
length
)
&&
list
}
<
Box
marginTop=
{
8
}
>
<
Button
variant=
"primary"
...
...
ui/privateTags/TransactionTagTable/TransactionTagListItem.tsx
0 → 100644
View file @
8b68dfdc
import
{
Tag
,
HStack
,
Text
,
Flex
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
TransactionTag
}
from
'
types/api/account
'
;
import
AccountListItemMobile
from
'
ui/shared/AccountListItemMobile
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
import
TransactionSnippet
from
'
ui/shared/TransactionSnippet
'
;
interface
Props
{
item
:
TransactionTag
;
onEditClick
:
(
data
:
TransactionTag
)
=>
void
;
onDeleteClick
:
(
data
:
TransactionTag
)
=>
void
;
}
const
TransactionTagListItem
=
({
item
,
onEditClick
,
onDeleteClick
}:
Props
)
=>
{
const
onItemEditClick
=
useCallback
(()
=>
{
return
onEditClick
(
item
);
},
[
item
,
onEditClick
]);
const
onItemDeleteClick
=
useCallback
(()
=>
{
return
onDeleteClick
(
item
);
},
[
item
,
onDeleteClick
]);
return
(
<
AccountListItemMobile
>
<
Flex
alignItems=
"flex-start"
flexDirection=
"column"
maxW=
"100%"
>
<
TransactionSnippet
hash=
{
item
.
transaction_hash
}
/>
<
HStack
spacing=
{
3
}
mt=
{
4
}
>
<
Text
fontSize=
"sm"
fontWeight=
{
500
}
>
Private tag
</
Text
>
<
Tag
variant=
"gray"
lineHeight=
"24px"
>
{
item
.
name
}
</
Tag
>
</
HStack
>
</
Flex
>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
AccountListItemMobile
>
);
};
export
default
React
.
memo
(
TransactionTagListItem
);
ui/privateTags/TransactionTagTable/TransactionTagTableItem.tsx
View file @
8b68dfdc
...
...
@@ -2,16 +2,14 @@ import {
Tag
,
Tr
,
Td
,
HStack
,
Tooltip
,
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
TransactionTag
}
from
'
types/api/account
'
;
import
AddressLinkWithTooltip
from
'
ui/shared/AddressLinkWithTooltip
'
;
import
DeleteButton
from
'
ui/shared/DeleteButton
'
;
import
EditButton
from
'
ui/shared/EditButton
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
import
TransactionSnippet
from
'
ui/shared/TransactionSnippet
'
;
interface
Props
{
item
:
TransactionTag
;
...
...
@@ -19,7 +17,7 @@ interface Props {
onDeleteClick
:
(
data
:
TransactionTag
)
=>
void
;
}
const
Address
TagTableItem
=
({
item
,
onEditClick
,
onDeleteClick
}:
Props
)
=>
{
const
Transaction
TagTableItem
=
({
item
,
onEditClick
,
onDeleteClick
}:
Props
)
=>
{
const
onItemEditClick
=
useCallback
(()
=>
{
return
onEditClick
(
item
);
},
[
item
,
onEditClick
]);
...
...
@@ -31,7 +29,7 @@ const AddressTagTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
return
(
<
Tr
alignItems=
"top"
key=
{
item
.
id
}
>
<
Td
>
<
AddressLinkWithTooltip
address
=
{
item
.
transaction_hash
}
/>
<
TransactionSnippet
hash
=
{
item
.
transaction_hash
}
/>
</
Td
>
<
Td
>
<
Tooltip
label=
{
item
.
name
}
>
...
...
@@ -41,13 +39,10 @@ const AddressTagTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
</
Tooltip
>
</
Td
>
<
Td
>
<
HStack
spacing=
{
6
}
>
<
EditButton
onClick=
{
onItemEditClick
}
/>
<
DeleteButton
onClick=
{
onItemDeleteClick
}
/>
</
HStack
>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
Td
>
</
Tr
>
);
};
export
default
Address
TagTableItem
;
export
default
Transaction
TagTableItem
;
ui/publicTags/DeletePublicTagModal.tsx
View file @
8b68dfdc
import
{
Flex
,
Text
,
FormControl
,
FormLabel
,
Textarea
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Text
,
FormControl
,
FormLabel
,
Textarea
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
{
useQueryClient
}
from
'
@tanstack/react-query
'
;
import
React
,
{
useCallback
,
useState
}
from
'
react
'
;
import
type
{
ChangeEvent
}
from
'
react
'
;
...
...
@@ -22,6 +22,7 @@ const DeletePublicTagModal: React.FC<Props> = ({ isOpen, onClose, data, onDelete
const
tags
=
data
.
tags
.
split
(
'
;
'
);
const
queryClient
=
useQueryClient
();
const
formBackgroundColor
=
useColorModeValue
(
'
white
'
,
'
gray.900
'
);
const
deleteApiKey
=
useCallback
(()
=>
{
const
body
=
JSON
.
stringify
({
remove_reason
:
reason
});
...
...
@@ -44,9 +45,9 @@ const DeletePublicTagModal: React.FC<Props> = ({ isOpen, onClose, data, onDelete
if
(
tags
.
length
===
1
)
{
text
=
(
<>
<
Text
display=
"
flex
"
>
Public tag
</
Text
>
<
Text
fontWeight=
"600"
whiteSpace=
"pre"
>
{
` "${ tags[0] }" `
}
</
Text
>
<
Text
>
will be removed.
</
Text
>
<
Text
display=
"
inline"
as=
"span
"
>
Public tag
</
Text
>
<
Text
fontWeight=
"600"
whiteSpace=
"pre"
as=
"span"
>
{
` "${ tags[0] }" `
}
</
Text
>
<
Text
as=
"span"
>
will be removed.
</
Text
>
</>
);
}
...
...
@@ -54,29 +55,29 @@ const DeletePublicTagModal: React.FC<Props> = ({ isOpen, onClose, data, onDelete
const
tagsText
:
Array
<
JSX
.
Element
|
string
>
=
[];
tags
.
forEach
((
tag
,
index
)
=>
{
if
(
index
<
tags
.
length
-
2
)
{
tagsText
.
push
(<
Text
fontWeight=
"600"
whiteSpace=
"pre"
>
{
` "${ tag }"`
}
</
Text
>);
tagsText
.
push
(<
Text
fontWeight=
"600"
whiteSpace=
"pre"
as=
"span"
>
{
` "${ tag }"`
}
</
Text
>);
tagsText
.
push
(
'
,
'
);
}
if
(
index
===
tags
.
length
-
2
)
{
tagsText
.
push
(<
Text
fontWeight=
"600"
whiteSpace=
"pre"
>
{
` "${ tag }" `
}
</
Text
>);
tagsText
.
push
(<
Text
fontWeight=
"600"
whiteSpace=
"pre"
as=
"span"
>
{
` "${ tag }" `
}
</
Text
>);
tagsText
.
push
(
'
and
'
);
}
if
(
index
===
tags
.
length
-
1
)
{
tagsText
.
push
(<
Text
fontWeight=
"600"
whiteSpace=
"pre"
>
{
` "${ tag }" `
}
</
Text
>);
tagsText
.
push
(<
Text
fontWeight=
"600"
whiteSpace=
"pre"
as=
"span"
>
{
` "${ tag }" `
}
</
Text
>);
}
});
text
=
(
<>
<
Text
>
Public tags
</
Text
>
{
tagsText
}
<
Text
>
will be removed.
</
Text
>
<
Text
as=
"span"
>
Public tags
</
Text
>
{
tagsText
}
<
Text
as=
"span"
>
will be removed.
</
Text
>
</>
);
}
return
(
<>
<
Fle
x
marginBottom=
{
12
}
>
<
Bo
x
marginBottom=
{
12
}
>
{
text
}
</
Fle
x
>
<
FormControl
variant=
"floating"
id=
"tag-delete"
>
</
Bo
x
>
<
FormControl
variant=
"floating"
id=
"tag-delete"
backgroundColor=
{
formBackgroundColor
}
>
<
Textarea
size=
"lg"
value=
{
reason
}
...
...
@@ -86,7 +87,7 @@ const DeletePublicTagModal: React.FC<Props> = ({ isOpen, onClose, data, onDelete
</
FormControl
>
</>
);
},
[
tags
,
reason
,
onFieldChange
]);
},
[
tags
,
reason
,
onFieldChange
,
formBackgroundColor
]);
return
(
<
DeleteModal
...
...
ui/publicTags/PublicTagTable/PublicTagListItem.tsx
0 → 100644
View file @
8b68dfdc
import
{
Tag
,
VStack
,
Text
,
HStack
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
PublicTag
}
from
'
types/api/account
'
;
import
AccountListItemMobile
from
'
ui/shared/AccountListItemMobile
'
;
import
AddressSnippet
from
'
ui/shared/AddressSnippet
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
import
TruncatedTextTooltip
from
'
ui/shared/TruncatedTextTooltip
'
;
interface
Props
{
item
:
PublicTag
;
onEditClick
:
(
data
:
PublicTag
)
=>
void
;
onDeleteClick
:
(
data
:
PublicTag
)
=>
void
;
}
const
PublicTagListItem
=
({
item
,
onEditClick
,
onDeleteClick
}:
Props
)
=>
{
const
onItemEditClick
=
useCallback
(()
=>
{
return
onEditClick
(
item
);
},
[
item
,
onEditClick
]);
const
onItemDeleteClick
=
useCallback
(()
=>
{
return
onDeleteClick
(
item
);
},
[
item
,
onDeleteClick
]);
return
(
<
AccountListItemMobile
>
<
VStack
spacing=
{
3
}
alignItems=
"flex-start"
maxW=
"100%"
>
<
VStack
spacing=
{
4
}
alignItems=
"unset"
maxW=
"100%"
>
{
item
.
addresses
.
map
((
address
)
=>
<
AddressSnippet
key=
{
address
}
address=
{
address
}
/>)
}
</
VStack
>
<
HStack
spacing=
{
3
}
>
<
Text
fontSize=
"sm"
fontWeight=
{
500
}
>
Public tags
</
Text
>
<
HStack
spacing=
{
2
}
alignItems=
"baseline"
>
{
item
.
tags
.
split
(
'
;
'
).
map
((
tag
)
=>
{
return
(
<
TruncatedTextTooltip
label=
{
tag
}
key=
{
tag
}
>
<
Tag
variant=
"gray"
lineHeight=
"24px"
>
{
tag
}
</
Tag
>
</
TruncatedTextTooltip
>
);
})
}
</
HStack
>
</
HStack
>
<
HStack
spacing=
{
3
}
>
<
Text
fontSize=
"sm"
fontWeight=
{
500
}
>
Status
</
Text
>
<
Text
fontSize=
"sm"
variant=
"secondary"
>
Submitted
</
Text
>
</
HStack
>
</
VStack
>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
AccountListItemMobile
>
);
};
export
default
React
.
memo
(
PublicTagListItem
);
ui/publicTags/PublicTagTable/PublicTagTableItem.tsx
View file @
8b68dfdc
import
{
Box
,
Tag
,
Tr
,
Td
,
HStack
,
VStack
,
Text
,
}
from
'
@chakra-ui/react
'
;
...
...
@@ -11,10 +9,8 @@ import React, { useCallback } from 'react';
import
type
{
PublicTag
}
from
'
types/api/account
'
;
import
AddressIcon
from
'
ui/shared/AddressIcon
'
;
import
AddressLinkWithTooltip
from
'
ui/shared/AddressLinkWithTooltip
'
;
import
DeleteButton
from
'
ui/shared/DeleteButton
'
;
import
EditButton
from
'
ui/shared/EditButton
'
;
import
AddressSnippet
from
'
ui/shared/AddressSnippet
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
import
TruncatedTextTooltip
from
'
ui/shared/TruncatedTextTooltip
'
;
interface
Props
{
...
...
@@ -36,18 +32,7 @@ const PublicTagTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
<
Tr
alignItems=
"top"
key=
{
item
.
id
}
>
<
Td
>
<
VStack
spacing=
{
4
}
alignItems=
"unset"
>
{
item
.
addresses
.
map
((
address
)
=>
{
return
(
<
HStack
spacing=
{
4
}
key=
{
address
}
overflow=
"hidden"
alignItems=
"start"
>
<
AddressIcon
address=
{
address
}
/>
<
Box
overflow=
"hidden"
>
<
AddressLinkWithTooltip
address=
{
address
}
/>
{
/* will be added later */
}
{
/* <Text fontSize="sm" variant="secondary" mt={ 0.5 }>Address Name</Text> */
}
</
Box
>
</
HStack
>
);
})
}
{
item
.
addresses
.
map
((
address
)
=>
<
AddressSnippet
key=
{
address
}
address=
{
address
}
/>)
}
</
VStack
>
</
Td
>
<
Td
>
...
...
@@ -69,10 +54,7 @@ const PublicTagTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
</
VStack
>
</
Td
>
<
Td
>
<
HStack
spacing=
{
6
}
>
<
EditButton
onClick=
{
onItemEditClick
}
/>
<
DeleteButton
onClick=
{
onItemDeleteClick
}
/>
</
HStack
>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
Td
>
</
Tr
>
);
...
...
ui/publicTags/PublicTagsData.tsx
View file @
8b68dfdc
import
{
Box
,
Text
,
Button
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Button
,
Skeleton
,
useDisclosure
}
from
'
@chakra-ui/react
'
;
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
React
,
{
useCallback
,
useState
}
from
'
react
'
;
import
type
{
PublicTags
,
PublicTag
}
from
'
types/api/account
'
;
import
fetch
from
'
lib/client/fetch
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
PublicTagListItem
from
'
ui/publicTags/PublicTagTable/PublicTagListItem
'
;
import
AccountPageDescription
from
'
ui/shared/AccountPageDescription
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
SkeletonAccountMobile
from
'
ui/shared/SkeletonAccountMobile
'
;
import
SkeletonTable
from
'
ui/shared/SkeletonTable
'
;
import
DeletePublicTagModal
from
'
./DeletePublicTagModal
'
;
...
...
@@ -19,6 +23,7 @@ type Props = {
const
PublicTagsData
=
({
changeToFormScreen
,
onTagDelete
}:
Props
)
=>
{
const
deleteModalProps
=
useDisclosure
();
const
[
deleteModalData
,
setDeleteModalData
]
=
useState
<
PublicTag
>
();
const
isMobile
=
useIsMobile
();
const
{
data
,
isLoading
,
isError
}
=
useQuery
<
unknown
,
unknown
,
PublicTags
>
([
'
public-tags
'
],
async
()
=>
await
fetch
(
'
/api/account/public-tags
'
));
...
...
@@ -41,32 +46,53 @@ const PublicTagsData = ({ changeToFormScreen, onTagDelete }: Props) => {
},
[
deleteModalProps
]);
const
description
=
(
<
Text
marginBottom=
{
12
}
>
<
AccountPageDescription
>
You can request a public category tag which is displayed to all Blockscout users.
Public tags may be added to contract or external addresses, and any associated transactions will inherit that tag.
Clicking a tag opens a page with related information and helps provide context and data organization.
Requests are sent to a moderator for review and approval. This process can take several days.
</
Text
>
</
AccountPageDescription
>
);
if
(
isLoading
)
{
return
(
const
loader
=
isMobile
?
<
SkeletonAccountMobile
/>
:
(
<>
{
description
}
<
SkeletonTable
columns=
{
[
'
50%
'
,
'
25%
'
,
'
25%
'
,
'
108px
'
]
}
/>
<
Skeleton
height=
"48px"
width=
"270px"
marginTop=
{
8
}
/>
</>
);
return
(
<>
{
description
}
{
loader
}
</>
);
}
if
(
isError
)
{
return
<
DataFetchAlert
/>;
}
const
list
=
isMobile
?
(
<
Box
>
{
data
.
map
((
item
)
=>
(
<
PublicTagListItem
item=
{
item
}
key=
{
item
.
id
}
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
))
}
</
Box
>
)
:
(
<
PublicTagTable
data=
{
data
}
onEditClick=
{
onItemEditClick
}
onDeleteClick=
{
onItemDeleteClick
}
/>
);
return
(
<>
{
description
}
{
data
.
length
>
0
&&
<
PublicTagTable
data=
{
data
}
onEditClick=
{
onItemEditClick
}
onDeleteClick=
{
onItemDeleteClick
}
/>
}
{
data
.
length
>
0
&&
list
}
<
Box
marginTop=
{
8
}
>
<
Button
variant=
"primary"
...
...
ui/publicTags/PublicTagsForm/PublicTagFormAddressInput.tsx
View file @
8b68dfdc
import
{
IconButton
,
Icon
}
from
'
@chakra-ui/react
'
;
import
{
IconButton
,
Icon
,
Flex
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
ControllerRenderProps
,
Control
,
FieldError
}
from
'
react-hook-form
'
;
import
{
Controller
}
from
'
react-hook-form
'
;
...
...
@@ -17,24 +17,25 @@ interface Props {
error
?:
FieldError
;
onAddFieldClick
:
(
e
:
React
.
SyntheticEvent
)
=>
void
;
onRemoveFieldClick
:
(
index
:
number
)
=>
(
e
:
React
.
SyntheticEvent
)
=>
void
;
size
?:
string
;
}
const
MAX_INPUTS_NUM
=
10
;
export
default
function
PublicTagFormAction
({
control
,
index
,
fieldsLength
,
error
,
onAddFieldClick
,
onRemoveFieldClick
}:
Props
)
{
export
default
function
PublicTagFormAction
({
control
,
index
,
fieldsLength
,
error
,
onAddFieldClick
,
onRemoveFieldClick
,
size
}:
Props
)
{
const
renderAddressInput
=
useCallback
(({
field
}:
{
field
:
ControllerRenderProps
<
Inputs
,
`addresses.
${
number
}
.address`
>
})
=>
{
return
(
<
AddressInput
<
Inputs
,
`
addresses
.
$
{
number
}
.
address
`
>
field=
{
field
}
error=
{
error
}
size=
"lg"
size=
{
size
}
placeholder="Smart contract / Address (0x...)"
/
>
);
}, [ error ]);
}, [ error
, size
]);
return (
<>
<
Flex
flexDir=
"column"
rowGap=
{
5
}
alignItems=
"flex-end"
>
<
Controller
name=
{
`addresses.${ index }.address`
}
control=
{
control
}
...
...
@@ -44,31 +45,34 @@ export default function PublicTagFormAction({ control, index, fieldsLength, erro
required
:
index
===
0
,
}
}
/>
{
index
===
fieldsLength
-
1
&&
fieldsLength
<
MAX_INPUTS_NUM
&&
(
<
Flex
columnGap=
{
5
}
position=
{
{
base
:
'
static
'
,
lg
:
'
absolute
'
}
}
left=
{
{
base
:
'
auto
'
,
lg
:
'
calc(100% + 20px)
'
}
}
h=
"100%"
alignItems=
"center"
>
{
fieldsLength
>
1
&&
(
<
IconButton
aria
-
label=
"add
"
aria
-
label=
"delete
"
variant=
"iconBorder"
w=
"30px"
h=
"30px"
onClick=
{
onAddFieldClick
}
icon=
{
<
Icon
as=
{
PlusIcon
}
w=
"20px"
h=
"20px"
/>
}
position=
"absolute"
right=
{
index
===
0
?
'
-50px
'
:
'
-100px
'
}
top=
"25px"
onClick=
{
onRemoveFieldClick
(
index
)
}
icon=
{
<
Icon
as=
{
MinusIcon
}
w=
"20px"
h=
"20px"
/>
}
/>
)
}
{
fieldsLength
>
1
&&
(
{
index
===
fieldsLength
-
1
&&
fieldsLength
<
MAX_INPUTS_NUM
&&
(
<
IconButton
aria
-
label=
"delete
"
aria
-
label=
"add
"
variant=
"iconBorder"
w=
"30px"
h=
"30px"
onClick=
{
onRemoveFieldClick
(
index
)
}
icon=
{
<
Icon
as=
{
MinusIcon
}
w=
"20px"
h=
"20px"
/>
}
position=
"absolute"
right=
"-50px"
top=
"25px"
onClick=
{
onAddFieldClick
}
icon=
{
<
Icon
as=
{
PlusIcon
}
w=
"20px"
h=
"20px"
/>
}
/>
)
}
</>
)
}
</
Flex
>
</
Flex
>
);
}
ui/publicTags/PublicTagsForm/PublicTagFormComment.tsx
View file @
8b68dfdc
...
...
@@ -12,23 +12,24 @@ const TEXT_INPUT_MAX_LENGTH = 255;
interface
Props
{
control
:
Control
<
Inputs
>
;
error
?:
FieldError
;
size
?:
string
;
}
export
default
function
PublicTagFormComment
({
control
,
error
}:
Props
)
{
export
default
function
PublicTagFormComment
({
control
,
error
,
size
}:
Props
)
{
const
renderComment
=
useCallback
(({
field
}:
{
field
:
ControllerRenderProps
<
Inputs
,
'
comment
'
>
})
=>
{
return
(
<
FormControl
variant=
"floating"
id=
{
field
.
name
}
size=
"lg"
isRequired
>
<
FormControl
variant=
"floating"
id=
{
field
.
name
}
size=
{
size
}
isRequired
>
<
Textarea
{
...
field
}
isInvalid=
{
Boolean
(
error
)
}
size=
"lg"
size=
{
size
}
/>
<
FormLabel
>
{
getPlaceholderWithError
(
'
Specify the reason for adding tags and color preference(s)
'
,
error
?.
message
)
}
</
FormLabel
>
</
FormControl
>
);
},
[
error
]);
},
[
error
,
size
]);
return
(
<
Controller
...
...
ui/publicTags/PublicTagsForm/PublicTagsForm.tsx
View file @
8b68dfdc
...
...
@@ -16,6 +16,7 @@ import type { PublicTags, PublicTag, PublicTagNew, PublicTagErrors } from 'types
import
type
{
ErrorType
}
from
'
lib/client/fetch
'
;
import
fetch
from
'
lib/client/fetch
'
;
import
getErrorMessage
from
'
lib/getErrorMessage
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
{
EMAIL_REGEXP
}
from
'
lib/validations/email
'
;
import
FormSubmitAlert
from
'
ui/shared/FormSubmitAlert
'
;
...
...
@@ -52,10 +53,12 @@ const placeholders = {
comment
:
'
Specify the reason for adding tags and color preference(s).
'
,
}
as
Record
<
Path
<
Inputs
>
,
string
>
;
const
ADDRESS_INPUT_BUTTONS_WIDTH
=
1
7
0
;
const
ADDRESS_INPUT_BUTTONS_WIDTH
=
1
0
0
;
const
PublicTagsForm
=
({
changeToDataScreen
,
data
}:
Props
)
=>
{
const
queryClient
=
useQueryClient
();
const
isMobile
=
useIsMobile
();
const
inputSize
=
isMobile
?
'
md
'
:
'
lg
'
;
const
{
control
,
handleSubmit
,
formState
:
{
errors
,
isValid
},
setError
}
=
useForm
<
Inputs
>
({
defaultValues
:
{
...
...
@@ -149,10 +152,10 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
},
[
changeToDataScreen
]);
return
(
<
Box
width=
{
`calc(100% - ${ ADDRESS_INPUT_BUTTONS_WIDTH }px)`
}
maxWidth=
"844px"
>
<
Box
width=
{
{
base
:
'
auto
'
,
lg
:
`calc(100% - ${ ADDRESS_INPUT_BUTTONS_WIDTH }px)`
}
}
maxWidth=
"844px"
>
{
isAlertVisible
&&
<
Box
mb=
{
4
}
><
FormSubmitAlert
/></
Box
>
}
<
Text
size=
"sm"
variant=
"secondary"
paddingBottom=
{
5
}
>
Company info
</
Text
>
<
Grid
templateColumns=
"1fr 1fr"
rowGap=
{
4
}
columnGap=
{
5
}
>
<
Grid
templateColumns=
{
{
base
:
'
1fr
'
,
lg
:
'
1fr 1fr
'
}
}
rowGap=
{
4
}
columnGap=
{
5
}
>
<
GridItem
>
<
PublicTagsFormInput
<
Inputs
>
fieldName="fullName"
...
...
@@ -160,6 +163,7 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
label=
{
placeholders
.
fullName
}
error=
{
errors
.
fullName
}
required
size=
{
inputSize
}
/
>
</
GridItem
>
<
GridItem
>
...
...
@@ -168,6 +172,7 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
control=
{
control
}
label=
{
placeholders
.
companyName
}
error=
{
errors
.
companyName
}
size=
{
inputSize
}
/
>
</
GridItem
>
<
GridItem
>
...
...
@@ -178,6 +183,7 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
pattern=
{
EMAIL_REGEXP
}
error=
{
errors
.
email
}
required
size=
{
inputSize
}
/
>
</
GridItem
>
<
GridItem
>
...
...
@@ -186,10 +192,11 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
control=
{
control
}
label=
{
placeholders
.
companyUrl
}
error=
{
errors
?.
companyUrl
}
size=
{
inputSize
}
/
>
</
GridItem
>
</
Grid
>
<
Box
marginTop=
{
4
}
marginBottom=
{
8
}
>
<
Box
marginTop=
{
{
base
:
5
,
lg
:
8
}
}
marginBottom=
{
{
base
:
5
,
lg
:
8
}
}
>
<
PublicTagFormAction
control=
{
control
}
/>
</
Box
>
<
Text
size=
"sm"
variant=
"secondary"
marginBottom=
{
5
}
>
Public tags (2 tags maximum, please use
"
;
"
as a divider)
</
Text
>
...
...
@@ -199,7 +206,9 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
control=
{
control
}
label=
{
placeholders
.
tags
}
error=
{
errors
.
tags
}
required/
>
required
size=
{
inputSize
}
/
>
</
Box
>
{
fields
.
map
((
field
,
index
)
=>
{
return
(
...
...
@@ -211,12 +220,13 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
fieldsLength=
{
fields
.
length
}
onAddFieldClick=
{
onAddFieldClick
}
onRemoveFieldClick=
{
onRemoveFieldClick
}
size=
{
inputSize
}
/>
</
Box
>
);
})
}
<
Box
marginBottom=
{
8
}
>
<
PublicTagFormComment
control=
{
control
}
error=
{
errors
.
comment
}
/>
<
PublicTagFormComment
control=
{
control
}
error=
{
errors
.
comment
}
size=
{
inputSize
}
/>
</
Box
>
<
HStack
spacing=
{
6
}
>
<
Button
...
...
ui/publicTags/PublicTagsForm/PublicTagsFormInput.tsx
View file @
8b68dfdc
...
...
@@ -14,6 +14,7 @@ interface Props<TInputs extends FieldValues> {
control
:
Control
<
TInputs
,
object
>
;
pattern
?:
RegExp
;
error
?:
FieldError
;
size
?:
string
;
}
export
default
function
PublicTagsFormInput
<
Inputs
extends
FieldValues
>
({
...
...
@@ -23,13 +24,14 @@ export default function PublicTagsFormInput<Inputs extends FieldValues>({
fieldName
,
pattern
,
error
,
size
,
}:
Props
<
Inputs
>
)
{
const
renderInput
=
useCallback
(({
field
}:
{
field
:
ControllerRenderProps
<
Inputs
,
typeof
fieldName
>
})
=>
{
return
(
<
FormControl
variant=
"floating"
id=
{
field
.
name
}
isRequired=
{
required
}
size=
"lg"
>
<
FormControl
variant=
"floating"
id=
{
field
.
name
}
isRequired=
{
required
}
size=
{
size
}
>
<
Input
{
...
field
}
size=
"lg"
size=
{
size
}
required=
{
required
}
isInvalid=
{
Boolean
(
error
)
}
maxLength=
{
TEXT_INPUT_MAX_LENGTH
}
...
...
@@ -37,7 +39,7 @@ export default function PublicTagsFormInput<Inputs extends FieldValues>({
<
FormLabel
>
{
getPlaceholderWithError
(
label
,
error
?.
message
)
}
</
FormLabel
>
</
FormControl
>
);
},
[
label
,
required
,
error
]);
},
[
label
,
required
,
error
,
size
]);
return
(
<
Controller
name=
{
fieldName
}
...
...
ui/shared/AccountListItemMobile.tsx
0 → 100644
View file @
8b68dfdc
import
{
VStack
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
interface
Props
{
children
:
React
.
ReactNode
;
}
const
AccountListItemMobile
=
({
children
}:
Props
)
=>
{
return
(
<
VStack
gap=
{
4
}
alignItems=
"flex-start"
paddingY=
{
6
}
borderColor=
{
useColorModeValue
(
'
blackAlpha.200
'
,
'
whiteAlpha.200
'
)
}
borderTopWidth=
"1px"
_last=
{
{
borderBottomWidth
:
'
1px
'
,
}
}
>
{
children
}
</
VStack
>
);
};
export
default
AccountListItemMobile
;
ui/shared/AccountPageDescription.tsx
0 → 100644
View file @
8b68dfdc
import
{
Text
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
const
AccountPageDescription
=
({
children
}:
{
children
:
React
.
ReactNode
})
=>
{
return
(
<
Text
marginBottom=
{
{
base
:
6
,
lg
:
12
}
}
>
{
children
}
</
Text
>
);
};
export
default
AccountPageDescription
;
ui/shared/AccountPageHeader.tsx
View file @
8b68dfdc
import
{
Heading
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
const
PageHeader
=
({
text
}:
{
text
:
string
})
=>
{
const
Account
PageHeader
=
({
text
}:
{
text
:
string
})
=>
{
return
(
<
Heading
as=
"h1"
size=
"lg"
marginBottom=
{
8
}
>
{
text
}
</
Heading
>
<
Heading
as=
"h1"
size=
"lg"
marginBottom=
{
{
base
:
6
,
lg
:
8
}
}
>
{
text
}
</
Heading
>
);
};
export
default
PageHeader
;
export
default
Account
PageHeader
;
ui/shared/AddressIcon.tsx
View file @
8b68dfdc
...
...
@@ -4,7 +4,7 @@ import Jazzicon, { jsNumberForAddress } from 'react-jazzicon';
const
AddressIcon
=
({
address
}:
{
address
:
string
})
=>
{
return
(
<
Box
width=
"24px"
>
<
Box
width=
"24px"
display=
"inline-flex"
>
<
Jazzicon
diameter=
{
24
}
seed=
{
jsNumberForAddress
(
address
)
}
/>
</
Box
>
);
...
...
ui/shared/AddressLinkWithTooltip.tsx
View file @
8b68dfdc
...
...
@@ -8,17 +8,28 @@ import CopyToClipboard from './CopyToClipboard';
const
FONT_WEIGHT
=
'
600
'
;
const
AddressLinkWithTooltip
=
({
address
}:
{
address
:
string
})
=>
{
type
Props
=
{
address
:
string
;
type
?:
'
address
'
|
'
transaction
'
;
}
const
AddressLinkWithTooltip
=
({
address
,
type
=
'
address
'
}:
Props
)
=>
{
const
basePath
=
useBasePath
();
const
url
=
basePath
+
'
/address/
'
+
address
+
'
/tokens#address-tabs
'
;
let
url
;
if
(
type
===
'
transaction
'
)
{
url
=
basePath
+
'
/tx/
'
+
address
;
}
else
{
url
=
basePath
+
'
/address/
'
+
address
+
'
/tokens#address-tabs
'
;
}
return
(
<
HStack
spacing=
{
2
}
alignContent=
"center"
overflow=
"hidden"
>
<
HStack
spacing=
{
2
}
alignContent=
"center"
overflow=
"hidden"
maxW=
"100%"
>
<
Link
href=
{
url
}
target=
"_blank"
overflow=
"hidden"
fontWeight=
{
FONT_WEIGHT
}
lineHeight=
"24px"
whiteSpace=
"nowrap"
>
<
AddressWithDots
address=
{
address
}
fontWeight=
{
FONT_WEIGHT
}
/>
</
Link
>
...
...
ui/shared/AddressSnippet.tsx
0 → 100644
View file @
8b68dfdc
import
{
Box
,
HStack
,
Text
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
AddressIcon
from
'
ui/shared/AddressIcon
'
;
import
AddressLinkWithTooltip
from
'
ui/shared/AddressLinkWithTooltip
'
;
interface
Props
{
address
:
string
;
subtitle
?:
string
;
}
const
AddressSnippet
=
({
address
,
subtitle
}:
Props
)
=>
{
return
(
<
HStack
spacing=
{
4
}
key=
{
address
}
overflow=
"hidden"
alignItems=
"start"
maxW=
"100%"
>
<
AddressIcon
address=
{
address
}
/>
<
Box
overflow=
"hidden"
>
<
AddressLinkWithTooltip
address=
{
address
}
/>
{
subtitle
&&
<
Text
fontSize=
"sm"
variant=
"secondary"
mt=
{
0.5
}
>
{
subtitle
}
</
Text
>
}
</
Box
>
</
HStack
>
);
};
export
default
React
.
memo
(
AddressSnippet
);
ui/shared/ApiKeySnippet.tsx
0 → 100644
View file @
8b68dfdc
import
{
Box
,
HStack
,
Icon
,
Flex
,
Text
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
keyIcon
from
'
icons/key.svg
'
;
import
CopyToClipboard
from
'
ui/shared/CopyToClipboard
'
;
interface
Props
{
apiKey
:
string
;
name
:
string
;
}
const
ApiKeySnippet
=
({
apiKey
,
name
}:
Props
)
=>
{
return
(
<
HStack
spacing=
{
2
}
alignItems=
"start"
>
<
Icon
as=
{
keyIcon
}
boxSize=
{
6
}
color=
{
useColorModeValue
(
'
gray.500
'
,
'
gray.400
'
)
}
/>
<
Box
>
<
Flex
alignItems=
{
{
base
:
'
flex-start
'
,
lg
:
'
center
'
}
}
>
<
Text
fontSize=
"md"
lineHeight=
{
6
}
fontWeight=
{
600
}
mr=
{
1
}
>
{
apiKey
}
</
Text
>
<
CopyToClipboard
text=
{
apiKey
}
/>
</
Flex
>
{
name
&&
<
Text
fontSize=
"sm"
variant=
"secondary"
mt=
{
1
}
>
{
name
}
</
Text
>
}
</
Box
>
</
HStack
>
);
};
export
default
React
.
memo
(
ApiKeySnippet
);
ui/shared/DeleteButton.tsx
deleted
100644 → 0
View file @
3d44ca80
import
{
Tooltip
,
IconButton
,
Icon
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
DeleteIcon
from
'
icons/delete.svg
'
;
type
Props
=
{
onClick
:
()
=>
void
;
}
const
DeleteButton
=
({
onClick
}:
Props
)
=>
{
const
onFocusCapture
=
useCallback
((
e
:
React
.
SyntheticEvent
)
=>
e
.
stopPropagation
(),
[]);
return
(
<
Tooltip
label=
"Delete"
>
<
IconButton
aria
-
label=
"delete"
variant=
"icon"
w=
"30px"
h=
"30px"
onClick=
{
onClick
}
icon=
{
<
Icon
as=
{
DeleteIcon
}
w=
"20px"
h=
"20px"
/>
}
onFocusCapture=
{
onFocusCapture
}
/>
</
Tooltip
>
);
};
export
default
DeleteButton
;
ui/shared/DeleteModal.tsx
View file @
8b68dfdc
...
...
@@ -54,7 +54,7 @@ const DeleteModal: React.FC<Props> = ({
},
[
setAlertVisible
,
mutation
]);
return
(
<
Modal
isOpen=
{
isOpen
}
onClose=
{
onModalClose
}
size=
"md"
>
<
Modal
isOpen=
{
isOpen
}
onClose=
{
onModalClose
}
size=
{
{
base
:
'
full
'
,
lg
:
'
md
'
}
}
>
<
ModalOverlay
/>
<
ModalContent
>
<
ModalHeader
fontWeight=
"500"
textStyle=
"h3"
>
{
title
}
</
ModalHeader
>
...
...
ui/shared/EditButton.tsx
deleted
100644 → 0
View file @
3d44ca80
import
{
Tooltip
,
IconButton
,
Icon
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
EditIcon
from
'
icons/edit.svg
'
;
type
Props
=
{
onClick
:
()
=>
void
;
}
const
EditButton
=
({
onClick
}:
Props
)
=>
{
const
onFocusCapture
=
useCallback
((
e
:
React
.
SyntheticEvent
)
=>
e
.
stopPropagation
(),
[]);
return
(
<
Tooltip
label=
"Edit"
>
<
IconButton
aria
-
label=
"edit"
variant=
"icon"
w=
"30px"
h=
"30px"
onClick=
{
onClick
}
icon=
{
<
Icon
as=
{
EditIcon
}
w=
"20px"
h=
"20px"
/>
}
onFocusCapture=
{
onFocusCapture
}
/>
</
Tooltip
>
);
};
export
default
EditButton
;
ui/shared/FormModal.tsx
View file @
8b68dfdc
...
...
@@ -39,14 +39,14 @@ export default function FormModal<TData>({
},
[
onClose
,
setAlertVisible
]);
return
(
<
Modal
isOpen=
{
isOpen
}
onClose=
{
onModalClose
}
size=
"md"
>
<
Modal
isOpen=
{
isOpen
}
onClose=
{
onModalClose
}
size=
{
{
base
:
'
full
'
,
lg
:
'
md
'
}
}
>
<
ModalOverlay
/>
<
ModalContent
>
<
ModalHeader
fontWeight=
"500"
textStyle=
"h3"
>
{
title
}
</
ModalHeader
>
<
ModalCloseButton
/>
<
ModalBody
mb=
{
0
}
>
{
(
isAlertVisible
||
text
)
&&
(
<
Box
marginBottom=
{
12
}
>
<
Box
marginBottom=
{
{
base
:
6
,
lg
:
12
}
}
>
{
text
&&
(
<
Text
lineHeight=
"30px"
mb=
{
3
}
>
{
text
}
...
...
ui/shared/SkeletonAccountMobile.tsx
0 → 100644
View file @
8b68dfdc
import
{
Box
,
Flex
,
Skeleton
,
SkeletonCircle
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
interface
Props
{
showFooterSlot
?:
boolean
;
}
const
SkeletonAccountMobile
=
({
showFooterSlot
}:
Props
)
=>
{
const
borderColor
=
useColorModeValue
(
'
blackAlpha.200
'
,
'
whiteAlpha.200
'
);
return
(
<
Box
>
{
Array
.
from
(
Array
(
2
)).
map
((
item
,
index
)
=>
(
<
Flex
key=
{
index
}
rowGap=
{
3
}
flexDirection=
"column"
paddingY=
{
6
}
borderTopWidth=
"1px"
borderColor=
{
borderColor
}
_first=
{
{
borderTopWidth
:
'
0
'
,
pt
:
'
0
'
,
}
}
>
<
Flex
columnGap=
{
2
}
w=
"100%"
alignItems=
"center"
>
<
SkeletonCircle
size=
"6"
flexShrink=
"0"
/>
<
Skeleton
h=
{
4
}
w=
"100%"
/>
</
Flex
>
<
Skeleton
h=
{
4
}
w=
"164px"
/>
<
Skeleton
h=
{
4
}
w=
"164px"
/>
<
Flex
columnGap=
{
3
}
mt=
{
7
}
>
{
showFooterSlot
&&
(
<
Flex
alignItems=
"center"
columnGap=
{
2
}
>
<
Skeleton
h=
{
4
}
w=
"164px"
/>
<
SkeletonCircle
size=
"6"
flexShrink=
"0"
/>
</
Flex
>
)
}
<
SkeletonCircle
size=
"6"
flexShrink=
"0"
ml=
"auto"
/>
<
SkeletonCircle
size=
"6"
flexShrink=
"0"
/>
</
Flex
>
</
Flex
>
))
}
</
Box
>
);
};
export
default
SkeletonAccountMobile
;
ui/shared/TableItemActionButtons.tsx
0 → 100644
View file @
8b68dfdc
import
{
Tooltip
,
IconButton
,
Icon
,
HStack
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
DeleteIcon
from
'
icons/delete.svg
'
;
import
EditIcon
from
'
icons/edit.svg
'
;
type
Props
=
{
onEditClick
:
()
=>
void
;
onDeleteClick
:
()
=>
void
;
}
const
TableItemActionButtons
=
({
onEditClick
,
onDeleteClick
}:
Props
)
=>
{
// prevent set focus on button when closing modal
const
onFocusCapture
=
useCallback
((
e
:
React
.
SyntheticEvent
)
=>
e
.
stopPropagation
(),
[]);
return
(
<
HStack
spacing=
{
6
}
alignSelf=
"flex-end"
>
<
Tooltip
label=
"Edit"
>
<
IconButton
aria
-
label=
"edit"
variant=
"icon"
w=
"30px"
h=
"30px"
onClick=
{
onEditClick
}
icon=
{
<
Icon
as=
{
EditIcon
}
w=
"20px"
h=
"20px"
/>
}
onFocusCapture=
{
onFocusCapture
}
/>
</
Tooltip
>
<
Tooltip
label=
"Delete"
>
<
IconButton
aria
-
label=
"delete"
variant=
"icon"
w=
"30px"
h=
"30px"
onClick=
{
onDeleteClick
}
icon=
{
<
Icon
as=
{
DeleteIcon
}
w=
"20px"
h=
"20px"
/>
}
onFocusCapture=
{
onFocusCapture
}
/>
</
Tooltip
>
</
HStack
>
);
};
export
default
React
.
memo
(
TableItemActionButtons
);
ui/shared/TransactionSnippet.tsx
0 → 100644
View file @
8b68dfdc
import
{
Box
,
HStack
,
Icon
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
transactionIcon
from
'
icons/transactions.svg
'
;
import
AddressLinkWithTooltip
from
'
ui/shared/AddressLinkWithTooltip
'
;
interface
Props
{
hash
:
string
;
}
const
TransactionSnippet
=
({
hash
}:
Props
)
=>
{
return
(
<
HStack
spacing=
{
2
}
overflow=
"hidden"
alignItems=
"start"
maxW=
"100%"
>
<
Icon
as=
{
transactionIcon
}
boxSize=
{
6
}
color=
{
useColorModeValue
(
'
gray.500
'
,
'
gray.400
'
)
}
/>
<
Box
overflow=
"hidden"
>
<
AddressLinkWithTooltip
address=
{
hash
}
type=
"transaction"
/>
</
Box
>
</
HStack
>
);
};
export
default
React
.
memo
(
TransactionSnippet
);
ui/watchlist/AddressModal/AddressForm.tsx
View file @
8b68dfdc
...
...
@@ -150,7 +150,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
return (
<>
<
Box
marginBottom=
{
5
}
marginTop=
{
5
}
>
<
Box
marginBottom=
{
5
}
>
<
Controller
name=
"address"
control=
{
control
}
...
...
@@ -178,7 +178,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
<
Box
marginBottom=
{
8
}
>
<
AddressFormNotifications
control=
{
control
}
/>
</
Box
>
<
Text
variant=
"secondary"
fontSize=
"sm"
marginBottom=
{
5
}
>
Notification methods
</
Text
>
<
Text
variant=
"secondary"
fontSize=
"sm"
marginBottom=
{
{
base
:
'
10px
'
,
lg
:
5
}
}
>
Notification methods
</
Text
>
<
Controller
name=
{
'
notification
'
as
Checkboxes
}
control=
{
control
}
...
...
ui/watchlist/AddressModal/AddressFormNotifications.tsx
View file @
8b68dfdc
...
...
@@ -20,13 +20,21 @@ export default function AddressFormNotifications<Inputs extends FieldValues, Che
), []);
return (
<
Grid
templateColumns=
"repeat(3, max-content)"
gap=
"20px 24px"
>
<
Grid
templateColumns=
{
{
base
:
'
repeat(2, max-content)
'
,
lg
:
'
repeat(3, max-content)
'
}
}
gap=
{
{
base
:
'
10px 24px
'
,
lg
:
'
20px 24px
'
}
}
>
{
NOTIFICATIONS
.
map
((
notification
:
string
,
index
:
number
)
=>
{
const
incomingFieldName
=
`notification_settings.${ notification }.incoming`
as
Checkboxes
;
const
outgoingFieldName
=
`notification_settings.${ notification }.outcoming`
as
Checkboxes
;
return
(
<
React
.
Fragment
key=
{
notification
}
>
<
GridItem
>
{
NOTIFICATIONS_NAMES
[
index
]
}
</
GridItem
>
<
GridItem
gridColumnStart=
{
{
base
:
1
,
lg
:
1
}
}
gridColumnEnd=
{
{
base
:
3
,
lg
:
1
}
}
_notFirst=
{
{
mt
:
{
base
:
3
,
lg
:
0
},
}
}
>
{
NOTIFICATIONS_NAMES
[
index
]
}
</
GridItem
>
<
GridItem
>
<
Controller
name=
{
incomingFieldName
}
...
...
ui/watchlist/DeleteAddressModal.tsx
View file @
8b68dfdc
...
...
@@ -5,6 +5,7 @@ import React, { useCallback } from 'react';
import
type
{
TWatchlistItem
,
TWatchlist
}
from
'
types/client/account
'
;
import
fetch
from
'
lib/client/fetch
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
DeleteModal
from
'
ui/shared/DeleteModal
'
;
type
Props
=
{
...
...
@@ -15,6 +16,7 @@ type Props = {
const
DeleteAddressModal
:
React
.
FC
<
Props
>
=
({
isOpen
,
onClose
,
data
})
=>
{
const
queryClient
=
useQueryClient
();
const
isMobile
=
useIsMobile
();
const
mutationFn
=
useCallback
(()
=>
{
return
fetch
(
`/api/account1/watchlist/
${
data
?.
id
}
`, { method: 'DELETE' });
...
...
@@ -29,10 +31,11 @@ const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
const address = data?.address_hash;
const renderModalContent = useCallback(() => {
const addressString = isMobile ? [ address.slice(0, 4), address.slice(-4) ].join('...') : address;
return (
<Text
display="flex">Address <Text fontWeight="600" whiteSpace="pre"> { address || 'address' } </Text>
will be deleted</Text>
<Text
>Address <Text fontWeight="600" as="span"> { addressString || 'address' }</Text>
will be deleted</Text>
);
}, [ address ]);
}, [ address
, isMobile
]);
return (
<DeleteModal
...
...
ui/watchlist/WatchlistTable/WatchListAddressItem.tsx
View file @
8b68dfdc
...
...
@@ -6,8 +6,7 @@ import type { TWatchlistItem } from 'types/client/account';
import
TokensIcon
from
'
icons/tokens.svg
'
;
// import WalletIcon from 'icons/wallet.svg';
import
{
nbsp
}
from
'
lib/html-entities
'
;
import
AddressIcon
from
'
ui/shared/AddressIcon
'
;
import
AddressLinkWithTooltip
from
'
ui/shared/AddressLinkWithTooltip
'
;
import
AddressSnippet
from
'
ui/shared/AddressSnippet
'
;
// now this component works only for xDAI
// for other networks later we will use config or smth
...
...
@@ -18,19 +17,18 @@ const WatchListAddressItem = ({ item }: {item: TWatchlistItem}) => {
const
nativeBalance
=
((
item
.
address_balance
||
0
)
/
10
**
DECIMALS
).
toFixed
(
1
);
const
nativeBalanceUSD
=
item
.
exchange_rate
?
`$
${
Number
(
nativeBalance
)
*
item
.
exchange_rate
}
USD`
:
'
N/A
'
;
const
infoItemsPaddingLeft
=
{
base
:
0
,
lg
:
10
};
return
(
<
HStack
spacing=
{
3
}
align=
"top"
>
<
AddressIcon
address=
{
item
.
address_hash
}
/>
<
VStack
spacing=
{
2
}
align=
"stretch"
overflow=
"hidden"
fontWeight=
{
500
}
color=
"gray.700"
>
<
AddressLinkWithTooltip
address=
{
item
.
address_hash
}
/>
<
HStack
spacing=
{
0
}
fontSize=
"sm"
h=
{
6
}
>
<
AddressSnippet
address=
{
item
.
address_hash
}
/>
<
HStack
spacing=
{
0
}
fontSize=
"sm"
h=
{
6
}
pl=
{
infoItemsPaddingLeft
}
>
<
Image
src=
"/xdai.png"
alt=
"chain-logo"
marginRight=
"10px"
w=
"16px"
h=
"16px"
/>
<
Text
color=
{
mainTextColor
}
>
{
`xDAI balance:${ nbsp }`
+
nativeBalance
}
</
Text
>
<
Text
variant=
"secondary"
>
{
`${ nbsp }(${ nativeBalanceUSD })`
}
</
Text
>
</
HStack
>
{
item
.
tokens_count
&&
(
<
HStack
spacing=
{
0
}
fontSize=
"sm"
h=
{
6
}
>
<
HStack
spacing=
{
0
}
fontSize=
"sm"
h=
{
6
}
pl=
{
infoItemsPaddingLeft
}
>
<
Icon
as=
{
TokensIcon
}
marginRight=
"10px"
w=
"17px"
h=
"16px"
/>
<
Text
color=
{
mainTextColor
}
>
{
`Tokens:${ nbsp }`
+
item
.
tokens_count
}
</
Text
>
{
/* api does not provide token prices */
}
...
...
@@ -40,14 +38,13 @@ const WatchListAddressItem = ({ item }: {item: TWatchlistItem}) => {
)
}
{
/* api does not provide token prices */
}
{
/* { item.address_balance && (
<HStack spacing={ 0 } fontSize="sm" h={ 6 }>
<HStack spacing={ 0 } fontSize="sm" h={ 6 }
pl={ infoItemsPaddingLeft }
>
<Icon as={ WalletIcon } marginRight="10px" w="16px" h="16px"/>
<Text color={ mainTextColor }>{ `Net worth:${ nbsp }` }</Text>
<Link href="#">{ `$${ item.totalUSD } USD` }</Link>
</HStack>
) } */
}
</
VStack
>
</
HStack
>
);
};
...
...
ui/watchlist/WatchlistTable/WatchListItem.tsx
0 → 100644
View file @
8b68dfdc
import
{
Tag
,
Box
,
Switch
,
Text
,
HStack
,
Flex
}
from
'
@chakra-ui/react
'
;
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
React
,
{
useCallback
,
useState
}
from
'
react
'
;
import
type
{
TWatchlistItem
}
from
'
types/client/account
'
;
import
AccountListItemMobile
from
'
ui/shared/AccountListItemMobile
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
import
WatchListAddressItem
from
'
./WatchListAddressItem
'
;
interface
Props
{
item
:
TWatchlistItem
;
onEditClick
:
(
data
:
TWatchlistItem
)
=>
void
;
onDeleteClick
:
(
data
:
TWatchlistItem
)
=>
void
;
}
const
WatchListItem
=
({
item
,
onEditClick
,
onDeleteClick
}:
Props
)
=>
{
const
[
notificationEnabled
,
setNotificationEnabled
]
=
useState
(
item
.
notification_methods
.
email
);
const
onItemEditClick
=
useCallback
(()
=>
{
return
onEditClick
(
item
);
},
[
item
,
onEditClick
]);
const
onItemDeleteClick
=
useCallback
(()
=>
{
return
onDeleteClick
(
item
);
},
[
item
,
onDeleteClick
]);
const
{
mutate
}
=
useMutation
(()
=>
{
const
data
=
{
...
item
,
notification_methods
:
{
email
:
!
notificationEnabled
}
};
return
fetch
(
`/api/account/watchlist/
${
item
.
id
}
`
,
{
method
:
'
PUT
'
,
body
:
JSON
.
stringify
(
data
)
});
},
{
onError
:
()
=>
{
// eslint-disable-next-line no-console
console
.
log
(
'
error
'
);
},
onSuccess
:
()
=>
{
setNotificationEnabled
(
prevState
=>
!
prevState
);
},
});
const
onSwitch
=
useCallback
(()
=>
{
return
mutate
();
},
[
mutate
]);
return
(
<
AccountListItemMobile
>
<
Box
maxW=
"100%"
>
<
WatchListAddressItem
item=
{
item
}
/>
<
HStack
spacing=
{
3
}
mt=
{
6
}
>
<
Text
fontSize=
"sm"
fontWeight=
{
500
}
>
Private tag
</
Text
>
<
Tag
variant=
"gray"
lineHeight=
"24px"
>
{
item
.
name
}
</
Tag
>
</
HStack
>
</
Box
>
<
Flex
alignItems=
"center"
justifyContent=
"space-between"
mt=
{
6
}
w=
"100%"
>
<
HStack
spacing=
{
3
}
>
<
Text
fontSize=
"sm"
fontWeight=
{
500
}
>
Email notification
</
Text
>
<
Switch
colorScheme=
"blue"
size=
"md"
isChecked=
{
notificationEnabled
}
onChange=
{
onSwitch
}
/>
</
HStack
>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
Flex
>
</
AccountListItemMobile
>
);
};
export
default
WatchListItem
;
ui/watchlist/WatchlistTable/WatchListTableItem.tsx
View file @
8b68dfdc
...
...
@@ -3,7 +3,6 @@ import {
Tr
,
Td
,
Switch
,
HStack
,
}
from
'
@chakra-ui/react
'
;
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
React
,
{
useCallback
,
useState
}
from
'
react
'
;
...
...
@@ -12,8 +11,7 @@ import type { TWatchlistItem } from 'types/client/account';
import
fetch
from
'
lib/client/fetch
'
;
import
useToast
from
'
lib/hooks/useToast
'
;
import
DeleteButton
from
'
ui/shared/DeleteButton
'
;
import
EditButton
from
'
ui/shared/EditButton
'
;
import
TableItemActionButtons
from
'
ui/shared/TableItemActionButtons
'
;
import
TruncatedTextTooltip
from
'
ui/shared/TruncatedTextTooltip
'
;
import
WatchListAddressItem
from
'
./WatchListAddressItem
'
;
...
...
@@ -89,10 +87,7 @@ const WatchlistTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
/>
</
Td
>
<
Td
>
<
HStack
spacing=
{
6
}
>
<
EditButton
onClick=
{
onItemEditClick
}
/>
<
DeleteButton
onClick=
{
onItemDeleteClick
}
/>
</
HStack
>
<
TableItemActionButtons
onDeleteClick=
{
onItemDeleteClick
}
onEditClick=
{
onItemEditClick
}
/>
</
Td
>
</
Tr
>
);
...
...
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