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
abdd2a51
Commit
abdd2a51
authored
May 10, 2023
by
tom
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
custom error screen for 403
parent
90a73c95
Changes
26
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
252 additions
and
109 deletions
+252
-109
resources.ts
lib/api/resources.ts
+3
-0
getErrorCause.ts
lib/errors/getErrorCause.ts
+0
-0
getErrorCauseStatusCode.ts
lib/errors/getErrorCauseStatusCode.ts
+1
-1
getErrorObj.ts
lib/errors/getErrorObj.ts
+15
-0
getErrorObjPayload.ts
lib/errors/getErrorObjPayload.ts
+23
-0
getErrorObjStatusCode.ts
lib/errors/getErrorObjStatusCode.ts
+11
-0
_app.tsx
pages/_app.tsx
+3
-3
api_key.tsx
pages/account/api_key.tsx
+4
-1
custom_abi.tsx
pages/account/custom_abi.tsx
+4
-1
public_tags_request.tsx
pages/account/public_tags_request.tsx
+4
-1
tag_address.tsx
pages/account/tag_address.tsx
+4
-1
watchlist.tsx
pages/account/watchlist.tsx
+4
-1
profile.tsx
pages/auth/profile.tsx
+4
-1
AddressFavoriteButton.tsx
ui/address/details/AddressFavoriteButton.tsx
+20
-1
ApiKeys.tsx
ui/pages/ApiKeys.tsx
+6
-10
CustomAbi.tsx
ui/pages/CustomAbi.tsx
+6
-10
MyProfile.tsx
ui/pages/MyProfile.tsx
+4
-6
PrivateTags.tsx
ui/pages/PrivateTags.tsx
+2
-3
PublicTags.tsx
ui/pages/PublicTags.tsx
+2
-3
Watchlist.tsx
ui/pages/Watchlist.tsx
+46
-51
PrivateAddressTags.tsx
ui/privateTags/PrivateAddressTags.tsx
+2
-3
PrivateTransactionTags.tsx
ui/privateTags/PrivateTransactionTags.tsx
+2
-3
PublicTagsData.tsx
ui/publicTags/PublicTagsData.tsx
+2
-3
AppErrorInvalidTxHash.tsx
ui/shared/AppError/AppErrorInvalidTxHash.tsx
+2
-2
AppErrorUnverifiedEmail.tsx
ui/shared/AppError/AppErrorUnverifiedEmail.tsx
+68
-0
Page.tsx
ui/shared/Page/Page.tsx
+10
-4
No files found.
lib/api/resources.ts
View file @
abdd2a51
...
...
@@ -63,6 +63,9 @@ export const RESOURCES = {
user_info
:
{
path
:
'
/api/account/v1/user/info
'
,
},
email_resend
:
{
path
:
'
/api/account/v1/email/resend
'
,
},
custom_abi
:
{
path
:
'
/api/account/v1/user/custom_abis/:id?
'
,
pathParams
:
[
'
id
'
as
const
],
...
...
lib/errors/getErrorCause.ts
x
→
lib/errors/getErrorCause.ts
View file @
abdd2a51
File moved
lib/errors/getError
StatusCode.tsx
→
lib/errors/getError
CauseStatusCode.ts
View file @
abdd2a51
import
getErrorCause
from
'
./getErrorCause
'
;
export
default
function
getErrorStatusCode
(
error
:
Error
|
undefined
):
number
|
undefined
{
export
default
function
getError
Cause
StatusCode
(
error
:
Error
|
undefined
):
number
|
undefined
{
const
cause
=
getErrorCause
(
error
);
return
cause
&&
'
status
'
in
cause
&&
typeof
cause
.
status
===
'
number
'
?
cause
.
status
:
undefined
;
}
lib/errors/getErrorObj.ts
0 → 100644
View file @
abdd2a51
export
default
function
getErrorObj
(
error
:
unknown
)
{
if
(
typeof
error
!==
'
object
'
)
{
return
;
}
if
(
Array
.
isArray
(
error
))
{
return
;
}
if
(
error
===
null
)
{
return
;
}
return
error
;
}
lib/errors/getErrorObjPayload.ts
0 → 100644
View file @
abdd2a51
import
getErrorObj
from
'
./getErrorObj
'
;
export
default
function
getErrorObjPayload
<
Payload
extends
object
>
(
error
:
unknown
):
Payload
|
undefined
{
const
errorObj
=
getErrorObj
(
error
);
if
(
!
errorObj
||
!
(
'
payload
'
in
errorObj
))
{
return
;
}
if
(
typeof
errorObj
.
payload
!==
'
object
'
)
{
return
;
}
if
(
errorObj
===
null
)
{
return
;
}
if
(
Array
.
isArray
(
errorObj
))
{
return
;
}
return
errorObj
.
payload
as
Payload
;
}
lib/errors/getErrorObjStatusCode.ts
0 → 100644
View file @
abdd2a51
import
getErrorObj
from
'
./getErrorObj
'
;
export
default
function
getErrorObjStatusCode
(
error
:
unknown
)
{
const
errorObj
=
getErrorObj
(
error
);
if
(
!
errorObj
||
!
(
'
statusCode
'
in
errorObj
)
||
typeof
errorObj
.
statusCode
!==
'
number
'
)
{
return
;
}
return
errorObj
.
statusCode
;
}
pages/_app.tsx
View file @
abdd2a51
...
...
@@ -9,7 +9,7 @@ import type { ResourceError } from 'lib/api/resources';
import
{
AppContextProvider
}
from
'
lib/appContext
'
;
import
{
Chakra
}
from
'
lib/Chakra
'
;
import
{
ScrollDirectionProvider
}
from
'
lib/contexts/scrollDirection
'
;
import
getError
StatusCode
from
'
lib/errors/getError
StatusCode
'
;
import
getError
CauseStatusCode
from
'
lib/errors/getErrorCause
StatusCode
'
;
import
useConfigSentry
from
'
lib/hooks/useConfigSentry
'
;
import
{
SocketProvider
}
from
'
lib/socket/context
'
;
import
theme
from
'
theme
'
;
...
...
@@ -27,7 +27,7 @@ function MyApp({ Component, pageProps }: AppProps) {
refetchOnWindowFocus
:
false
,
retry
:
(
failureCount
,
_error
)
=>
{
const
error
=
_error
as
ResourceError
<
{
status
:
number
}
>
;
const
status
=
error
?.
status
||
error
?.
payload
?.
status
;
const
status
=
error
?.
payload
?.
status
||
error
?.
status
;
if
(
status
&&
status
>=
400
&&
status
<
500
)
{
// don't do retry for client error responses
return
false
;
...
...
@@ -40,7 +40,7 @@ function MyApp({ Component, pageProps }: AppProps) {
}));
const
renderErrorScreen
=
React
.
useCallback
((
error
?:
Error
)
=>
{
const
statusCode
=
getErrorStatusCode
(
error
);
const
statusCode
=
getError
Cause
StatusCode
(
error
);
return
(
<
AppError
...
...
pages/account/api_key.tsx
View file @
abdd2a51
...
...
@@ -4,13 +4,16 @@ import React from 'react';
import
getNetworkTitle
from
'
lib/networks/getNetworkTitle
'
;
import
ApiKeys
from
'
ui/pages/ApiKeys
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
const
ApiKeysPage
:
NextPage
=
()
=>
{
const
title
=
getNetworkTitle
();
return
(
<>
<
Head
><
title
>
{
title
}
</
title
></
Head
>
<
ApiKeys
/>
<
Page
>
<
ApiKeys
/>
</
Page
>
</>
);
};
...
...
pages/account/custom_abi.tsx
View file @
abdd2a51
...
...
@@ -4,13 +4,16 @@ import React from 'react';
import
getNetworkTitle
from
'
lib/networks/getNetworkTitle
'
;
import
CustomAbi
from
'
ui/pages/CustomAbi
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
const
CustomAbiPage
:
NextPage
=
()
=>
{
const
title
=
getNetworkTitle
();
return
(
<>
<
Head
><
title
>
{
title
}
</
title
></
Head
>
<
CustomAbi
/>
<
Page
>
<
CustomAbi
/>
</
Page
>
</>
);
};
...
...
pages/account/public_tags_request.tsx
View file @
abdd2a51
...
...
@@ -4,13 +4,16 @@ import React from 'react';
import
getNetworkTitle
from
'
lib/networks/getNetworkTitle
'
;
import
PublicTags
from
'
ui/pages/PublicTags
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
const
PublicTagsPage
:
NextPage
=
()
=>
{
const
title
=
getNetworkTitle
();
return
(
<>
<
Head
><
title
>
{
title
}
</
title
></
Head
>
<
PublicTags
/>
<
Page
>
<
PublicTags
/>
</
Page
>
</>
);
};
...
...
pages/account/tag_address.tsx
View file @
abdd2a51
...
...
@@ -4,13 +4,16 @@ import React from 'react';
import
getNetworkTitle
from
'
lib/networks/getNetworkTitle
'
;
import
PrivateTags
from
'
ui/pages/PrivateTags
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
const
AddressTagsPage
:
NextPage
=
()
=>
{
const
title
=
getNetworkTitle
();
return
(
<>
<
Head
><
title
>
{
title
}
</
title
></
Head
>
<
PrivateTags
/>
<
Page
>
<
PrivateTags
/>
</
Page
>
</>
);
};
...
...
pages/account/watchlist.tsx
View file @
abdd2a51
...
...
@@ -4,6 +4,7 @@ import React from 'react';
import
getNetworkTitle
from
'
lib/networks/getNetworkTitle
'
;
import
WatchList
from
'
ui/pages/Watchlist
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
const
WatchListPage
:
NextPage
=
()
=>
{
const
title
=
getNetworkTitle
();
...
...
@@ -12,7 +13,9 @@ const WatchListPage: NextPage = () => {
<
Head
>
<
title
>
{
title
}
</
title
>
</
Head
>
<
WatchList
/>
<
Page
>
<
WatchList
/>
</
Page
>
</>
);
};
...
...
pages/auth/profile.tsx
View file @
abdd2a51
...
...
@@ -3,12 +3,15 @@ import Head from 'next/head';
import
React
from
'
react
'
;
import
MyProfile
from
'
ui/pages/MyProfile
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
const
MyProfilePage
:
NextPage
=
()
=>
{
return
(
<>
<
Head
><
title
>
My profile
</
title
></
Head
>
<
MyProfile
/>
<
Page
>
<
MyProfile
/>
</
Page
>
</>
);
};
...
...
ui/address/details/AddressFavoriteButton.tsx
View file @
abdd2a51
...
...
@@ -7,10 +7,12 @@ import type { UserInfo } from 'types/api/account';
import
starFilledIcon
from
'
icons/star_filled.svg
'
;
import
starOutlineIcon
from
'
icons/star_outline.svg
'
;
import
type
{
ResourceError
}
from
'
lib/api/resources
'
;
import
{
resourceKey
}
from
'
lib/api/resources
'
;
import
{
getResourceKey
}
from
'
lib/api/useApiQuery
'
;
import
useLoginUrl
from
'
lib/hooks/useLoginUrl
'
;
import
usePreventFocusAfterModalClosing
from
'
lib/hooks/usePreventFocusAfterModalClosing
'
;
import
useToast
from
'
lib/hooks/useToast
'
;
import
WatchlistAddModal
from
'
ui/watchlist/AddressModal/AddressModal
'
;
import
DeleteAddressModal
from
'
ui/watchlist/DeleteAddressModal
'
;
...
...
@@ -25,18 +27,35 @@ const AddressFavoriteButton = ({ className, hash, watchListId }: Props) => {
const
deleteModalProps
=
useDisclosure
();
const
queryClient
=
useQueryClient
();
const
router
=
useRouter
();
const
toast
=
useToast
();
const
profileData
=
queryClient
.
getQueryData
<
UserInfo
>
([
resourceKey
(
'
user_info
'
)
]);
const
profileState
=
queryClient
.
getQueryState
<
unknown
,
ResourceError
<
{
message
:
string
}
>>
([
resourceKey
(
'
user_info
'
)
]);
const
isAuth
=
Boolean
(
profileData
);
const
loginUrl
=
useLoginUrl
();
const
handleClick
=
React
.
useCallback
(()
=>
{
if
(
profileState
?.
error
?.
status
===
403
)
{
const
isUnverifiedEmail
=
profileState
.
error
.
payload
?.
message
.
includes
(
'
Unverified email
'
);
if
(
isUnverifiedEmail
)
{
toast
({
position
:
'
top-right
'
,
title
:
'
Error
'
,
description
:
'
Unable to add address to watch list. Please go to the watch list page instead.
'
,
status
:
'
error
'
,
variant
:
'
subtle
'
,
isClosable
:
true
,
});
return
;
}
}
if
(
!
isAuth
)
{
window
.
location
.
assign
(
loginUrl
);
return
;
}
watchListId
?
deleteModalProps
.
onOpen
()
:
addModalProps
.
onOpen
();
},
[
addModalProps
,
deleteModalProps
,
watchListId
,
isAuth
,
loginUrl
]);
},
[
profileState
?.
error
,
isAuth
,
watchListId
,
deleteModalProps
,
addModalProps
,
loginUrl
,
toast
]);
const
handleAddOrDeleteSuccess
=
React
.
useCallback
(
async
()
=>
{
const
queryKey
=
getResourceKey
(
'
address
'
,
{
pathParams
:
{
hash
:
router
.
query
.
hash
?.
toString
()
}
});
...
...
ui/pages/ApiKeys.tsx
View file @
abdd2a51
...
...
@@ -12,8 +12,6 @@ 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
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
import
SkeletonListAccount
from
'
ui/shared/skeletons/SkeletonListAccount
'
;
import
SkeletonTable
from
'
ui/shared/skeletons/SkeletonTable
'
;
...
...
@@ -29,7 +27,7 @@ const ApiKeysPage: React.FC = () => {
const
[
apiKeyModalData
,
setApiKeyModalData
]
=
useState
<
ApiKey
>
();
const
[
deleteModalData
,
setDeleteModalData
]
=
useState
<
ApiKey
>
();
const
{
data
,
isLoading
,
isError
}
=
useApiQuery
(
'
api_keys
'
);
const
{
data
,
isLoading
,
isError
,
error
}
=
useApiQuery
(
'
api_keys
'
);
const
onEditClick
=
useCallback
((
data
:
ApiKey
)
=>
{
setApiKeyModalData
(
data
);
...
...
@@ -76,7 +74,7 @@ const ApiKeysPage: React.FC = () => {
}
if
(
isError
)
{
return
<
DataFetchAlert
/>
;
throw
new
Error
(
'
API keys fetch error
'
,
{
cause
:
error
})
;
}
const
list
=
isMobile
?
(
...
...
@@ -130,12 +128,10 @@ const ApiKeysPage: React.FC = () => {
})();
return
(
<
Page
>
<
Box
h=
"100%"
>
<
PageTitle
text=
"API keys"
/>
{
content
}
</
Box
>
</
Page
>
<>
<
PageTitle
text=
"API keys"
/>
{
content
}
</>
);
};
...
...
ui/pages/CustomAbi.tsx
View file @
abdd2a51
...
...
@@ -11,8 +11,6 @@ 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
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
import
SkeletonListAccount
from
'
ui/shared/skeletons/SkeletonListAccount
'
;
import
SkeletonTable
from
'
ui/shared/skeletons/SkeletonTable
'
;
...
...
@@ -26,7 +24,7 @@ const CustomAbiPage: React.FC = () => {
const
[
customAbiModalData
,
setCustomAbiModalData
]
=
useState
<
CustomAbi
>
();
const
[
deleteModalData
,
setDeleteModalData
]
=
useState
<
CustomAbi
>
();
const
{
data
,
isLoading
,
isError
}
=
useApiQuery
(
'
custom_abi
'
);
const
{
data
,
isLoading
,
isError
,
error
}
=
useApiQuery
(
'
custom_abi
'
);
const
onEditClick
=
useCallback
((
data
:
CustomAbi
)
=>
{
setCustomAbiModalData
(
data
);
...
...
@@ -72,7 +70,7 @@ const CustomAbiPage: React.FC = () => {
}
if
(
isError
)
{
return
<
DataFetchAlert
/>
;
throw
new
Error
(
'
Custom ABI fetch error
'
,
{
cause
:
error
})
;
}
const
list
=
isMobile
?
(
...
...
@@ -113,12 +111,10 @@ const CustomAbiPage: React.FC = () => {
})();
return
(
<
Page
>
<
Box
h=
"100%"
>
<
PageTitle
text=
"Custom ABI"
/>
{
content
}
</
Box
>
</
Page
>
<>
<
PageTitle
text=
"Custom ABI"
/>
{
content
}
</>
);
};
...
...
ui/pages/MyProfile.tsx
View file @
abdd2a51
...
...
@@ -4,13 +4,11 @@ import React from 'react';
import
useFetchProfileInfo
from
'
lib/hooks/useFetchProfileInfo
'
;
import
useRedirectForInvalidAuthToken
from
'
lib/hooks/useRedirectForInvalidAuthToken
'
;
import
ContentLoader
from
'
ui/shared/ContentLoader
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
import
UserAvatar
from
'
ui/shared/UserAvatar
'
;
const
MyProfile
=
()
=>
{
const
{
data
,
isLoading
,
isError
,
isFetched
}
=
useFetchProfileInfo
();
const
{
data
,
isLoading
,
isError
,
error
,
isFetched
}
=
useFetchProfileInfo
();
useRedirectForInvalidAuthToken
();
const
content
=
(()
=>
{
...
...
@@ -19,7 +17,7 @@ const MyProfile = () => {
}
if
(
isError
)
{
return
<
DataFetchAlert
/>
;
throw
new
Error
(
'
My profile fetch error
'
,
{
cause
:
error
})
;
}
return
(
...
...
@@ -54,10 +52,10 @@ const MyProfile = () => {
})();
return
(
<
Page
>
<>
<
PageTitle
text=
"My profile"
/>
{
content
}
</
Page
>
</>
);
};
...
...
ui/pages/PrivateTags.tsx
View file @
abdd2a51
...
...
@@ -5,7 +5,6 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import
useRedirectForInvalidAuthToken
from
'
lib/hooks/useRedirectForInvalidAuthToken
'
;
import
PrivateAddressTags
from
'
ui/privateTags/PrivateAddressTags
'
;
import
PrivateTransactionTags
from
'
ui/privateTags/PrivateTransactionTags
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
import
RoutedTabs
from
'
ui/shared/RoutedTabs/RoutedTabs
'
;
...
...
@@ -18,10 +17,10 @@ const PrivateTags = () => {
useRedirectForInvalidAuthToken
();
return
(
<
Page
>
<>
<
PageTitle
text=
"Private tags"
/>
<
RoutedTabs
tabs=
{
TABS
}
/>
</
Page
>
</>
);
};
...
...
ui/pages/PublicTags.tsx
View file @
abdd2a51
...
...
@@ -10,7 +10,6 @@ import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthT
import
useToast
from
'
lib/hooks/useToast
'
;
import
PublicTagsData
from
'
ui/publicTags/PublicTagsData
'
;
import
PublicTagsForm
from
'
ui/publicTags/PublicTagsForm/PublicTagsForm
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
type
TScreen
=
'
data
'
|
'
form
'
;
...
...
@@ -78,7 +77,7 @@ const PublicTagsComponent: React.FC = () => {
}
return
(
<
Page
>
<>
{
screen
===
'
form
'
&&
(
<
Link
display=
"inline-flex"
alignItems=
"center"
mb=
{
6
}
onClick=
{
onGoBack
}
>
<
Icon
as=
{
eastArrowIcon
}
boxSize=
{
6
}
transform=
"rotate(180deg)"
/>
...
...
@@ -87,7 +86,7 @@ const PublicTagsComponent: React.FC = () => {
)
}
<
PageTitle
text=
{
header
}
display=
{
{
base
:
'
block
'
,
lg
:
'
inline-flex
'
}
}
ml=
{
{
base
:
0
,
lg
:
3
}
}
/>
{
content
}
</
Page
>
</>
);
};
...
...
ui/pages/Watchlist.tsx
View file @
abdd2a51
...
...
@@ -10,8 +10,6 @@ import useApiFetch from 'lib/api/useApiFetch';
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
useRedirectForInvalidAuthToken
from
'
lib/hooks/useRedirectForInvalidAuthToken
'
;
import
AccountPageDescription
from
'
ui/shared/AccountPageDescription
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
import
SkeletonListAccount
from
'
ui/shared/skeletons/SkeletonListAccount
'
;
import
SkeletonTable
from
'
ui/shared/skeletons/SkeletonTable
'
;
...
...
@@ -22,31 +20,27 @@ import WatchlistTable from 'ui/watchlist/WatchlistTable/WatchlistTable';
const
WatchList
:
React
.
FC
=
()
=>
{
const
apiFetch
=
useApiFetch
();
const
{
data
,
isLoading
,
isError
}
=
useQuery
<
unknown
,
unknown
,
TWatchlist
>
([
resourceKey
(
'
watchlist
'
)
],
async
()
=>
{
try
{
const
watchlistAddresses
=
await
apiFetch
<
'
watchlist
'
,
Array
<
WatchlistAddress
>>
(
'
watchlist
'
);
const
{
data
,
isLoading
,
isError
,
error
}
=
useQuery
<
unknown
,
unknown
,
TWatchlist
>
([
resourceKey
(
'
watchlist
'
)
],
async
()
=>
{
const
watchlistAddresses
=
await
apiFetch
<
'
watchlist
'
,
Array
<
WatchlistAddress
>>
(
'
watchlist
'
);
if
(
!
Array
.
isArray
(
watchlistAddresses
))
{
throw
Error
();
}
const
watchlistTokens
=
await
Promise
.
all
(
watchlistAddresses
.
map
(({
address
})
=>
{
if
(
!
address
?.
hash
)
{
return
Promise
.
resolve
(
0
);
}
return
apiFetch
<
'
old_api
'
,
WatchlistTokensResponse
>
(
'
old_api
'
,
{
queryParams
:
{
address
:
address
.
hash
,
module
:
'
account
'
,
action
:
'
tokenlist
'
}
})
.
then
((
response
)
=>
{
if
(
'
result
'
in
response
&&
Array
.
isArray
(
response
.
result
))
{
return
response
.
result
.
length
;
}
return
0
;
});
}));
return
watchlistAddresses
.
map
((
item
,
index
)
=>
({
...
item
,
tokens_count
:
watchlistTokens
[
index
]
}));
}
catch
(
error
)
{
return
error
;
if
(
!
Array
.
isArray
(
watchlistAddresses
))
{
return
;
}
const
watchlistTokens
=
await
Promise
.
all
(
watchlistAddresses
.
map
(({
address
})
=>
{
if
(
!
address
?.
hash
)
{
return
Promise
.
resolve
(
0
);
}
return
apiFetch
<
'
old_api
'
,
WatchlistTokensResponse
>
(
'
old_api
'
,
{
queryParams
:
{
address
:
address
.
hash
,
module
:
'
account
'
,
action
:
'
tokenlist
'
}
})
.
then
((
response
)
=>
{
if
(
'
result
'
in
response
&&
Array
.
isArray
(
response
.
result
))
{
return
response
.
result
.
length
;
}
return
0
;
});
}));
return
watchlistAddresses
.
map
((
item
,
index
)
=>
({
...
item
,
tokens_count
:
watchlistTokens
[
index
]
}));
});
const
queryClient
=
useQueryClient
();
...
...
@@ -96,24 +90,27 @@ const WatchList: React.FC = () => {
</
AccountPageDescription
>
);
let
content
;
if
(
isLoading
&&
!
data
)
{
const
loader
=
isMobile
?
<
SkeletonListAccount
showFooterSlot
/>
:
(
<>
<
SkeletonTable
columns=
{
[
'
70%
'
,
'
30%
'
,
'
160px
'
,
'
108px
'
]
}
/>
<
Skeleton
height=
"44px"
width=
"156px"
marginTop=
{
8
}
/>
</>
);
if
(
isError
)
{
throw
new
Error
(
'
Watch list fetch error
'
,
{
cause
:
error
});
}
const
content
=
(()
=>
{
if
(
isLoading
&&
!
data
)
{
const
loader
=
isMobile
?
<
SkeletonListAccount
showFooterSlot
/>
:
(
<>
<
SkeletonTable
columns=
{
[
'
70%
'
,
'
30%
'
,
'
160px
'
,
'
108px
'
]
}
/>
<
Skeleton
height=
"44px"
width=
"156px"
marginTop=
{
8
}
/>
</>
);
return
(
<>
{
description
}
{
loader
}
</>
);
}
content
=
(
<>
{
description
}
{
loader
}
</>
);
}
else
if
(
isError
)
{
content
=
<
DataFetchAlert
/>;
}
else
{
const
list
=
isMobile
?
(
<
Box
>
{
data
.
map
((
item
)
=>
(
...
...
@@ -133,7 +130,7 @@ const WatchList: React.FC = () => {
/>
);
content
=
(
return
(
<>
{
description
}
{
Boolean
(
data
?.
length
)
&&
list
}
...
...
@@ -142,7 +139,7 @@ const WatchList: React.FC = () => {
size=
"lg"
onClick=
{
addressModalProps
.
onOpen
}
>
Add address
Add address
</
Button
>
</
Box
>
<
AddressModal
...
...
@@ -162,15 +159,13 @@ const WatchList: React.FC = () => {
)
}
</>
);
}
}
)();
return
(
<
Page
>
<
Box
h=
"100%"
>
<
PageTitle
text=
"Watch list"
/>
{
content
}
</
Box
>
</
Page
>
<>
<
PageTitle
text=
"Watch list"
/>
{
content
}
</>
);
};
...
...
ui/privateTags/PrivateAddressTags.tsx
View file @
abdd2a51
...
...
@@ -7,7 +7,6 @@ import useApiQuery from 'lib/api/useApiQuery';
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
{
PRIVATE_TAG_ADDRESS
}
from
'
stubs/account
'
;
import
AccountPageDescription
from
'
ui/shared/AccountPageDescription
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
AddressModal
from
'
./AddressModal/AddressModal
'
;
import
AddressTagListItem
from
'
./AddressTagTable/AddressTagListItem
'
;
...
...
@@ -15,7 +14,7 @@ import AddressTagTable from './AddressTagTable/AddressTagTable';
import
DeletePrivateTagModal
from
'
./DeletePrivateTagModal
'
;
const
PrivateAddressTags
=
()
=>
{
const
{
data
:
addressTagsData
,
isError
,
isPlaceholderData
}
=
useApiQuery
(
'
private_tags_address
'
,
{
const
{
data
:
addressTagsData
,
isError
,
error
,
isPlaceholderData
}
=
useApiQuery
(
'
private_tags_address
'
,
{
queryOptions
:
{
refetchOnMount
:
false
,
placeholderData
:
Array
(
3
).
fill
(
PRIVATE_TAG_ADDRESS
),
...
...
@@ -50,7 +49,7 @@ const PrivateAddressTags = () => {
},
[
deleteModalProps
]);
if
(
isError
)
{
return
<
DataFetchAlert
/>
;
throw
new
Error
(
'
Private tags fetch error
'
,
{
cause
:
error
})
;
}
const
list
=
isMobile
?
(
...
...
ui/privateTags/PrivateTransactionTags.tsx
View file @
abdd2a51
...
...
@@ -6,7 +6,6 @@ import type { TransactionTag } from 'types/api/account';
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
useIsMobile
from
'
lib/hooks/useIsMobile
'
;
import
AccountPageDescription
from
'
ui/shared/AccountPageDescription
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
SkeletonListAccount
from
'
ui/shared/skeletons/SkeletonListAccount
'
;
import
SkeletonTable
from
'
ui/shared/skeletons/SkeletonTable
'
;
...
...
@@ -16,7 +15,7 @@ import TransactionTagListItem from './TransactionTagTable/TransactionTagListItem
import
TransactionTagTable
from
'
./TransactionTagTable/TransactionTagTable
'
;
const
PrivateTransactionTags
=
()
=>
{
const
{
data
:
transactionTagsData
,
isLoading
,
isError
}
=
useApiQuery
(
'
private_tags_tx
'
,
{
queryOptions
:
{
refetchOnMount
:
false
}
});
const
{
data
:
transactionTagsData
,
isLoading
,
isError
,
error
}
=
useApiQuery
(
'
private_tags_tx
'
,
{
queryOptions
:
{
refetchOnMount
:
false
}
});
const
transactionModalProps
=
useDisclosure
();
const
deleteModalProps
=
useDisclosure
();
...
...
@@ -69,7 +68,7 @@ const PrivateTransactionTags = () => {
}
if
(
isError
)
{
return
<
DataFetchAlert
/>
;
throw
new
Error
(
'
Private tags fetch error
'
,
{
cause
:
error
})
;
}
const
list
=
isMobile
?
(
...
...
ui/publicTags/PublicTagsData.tsx
View file @
abdd2a51
...
...
@@ -7,7 +7,6 @@ import useApiQuery from 'lib/api/useApiQuery';
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
SkeletonListAccount
from
'
ui/shared/skeletons/SkeletonListAccount
'
;
import
SkeletonTable
from
'
ui/shared/skeletons/SkeletonTable
'
;
...
...
@@ -24,7 +23,7 @@ const PublicTagsData = ({ changeToFormScreen, onTagDelete }: Props) => {
const
[
deleteModalData
,
setDeleteModalData
]
=
useState
<
PublicTag
>
();
const
isMobile
=
useIsMobile
();
const
{
data
,
isLoading
,
isError
}
=
useApiQuery
(
'
public_tags
'
);
const
{
data
,
isLoading
,
isError
,
error
}
=
useApiQuery
(
'
public_tags
'
);
const
onDeleteModalClose
=
useCallback
(()
=>
{
setDeleteModalData
(
undefined
);
...
...
@@ -70,7 +69,7 @@ const PublicTagsData = ({ changeToFormScreen, onTagDelete }: Props) => {
}
if
(
isError
)
{
return
<
DataFetchAlert
/>
;
throw
new
Error
(
'
Public tags fetch error
'
,
{
cause
:
error
})
;
}
const
list
=
isMobile
?
(
...
...
ui/shared/ErrorInvalidTxHash.tsx
→
ui/shared/
AppError/App
ErrorInvalidTxHash.tsx
View file @
abdd2a51
...
...
@@ -4,7 +4,7 @@ import React from 'react';
import
txIcon
from
'
icons/transactions.svg
'
;
const
ErrorInvalidTxHash
=
()
=>
{
const
App
ErrorInvalidTxHash
=
()
=>
{
const
textColor
=
useColorModeValue
(
'
gray.500
'
,
'
gray.400
'
);
const
snippet
=
{
borderColor
:
useColorModeValue
(
'
blackAlpha.300
'
,
'
whiteAlpha.300
'
),
...
...
@@ -54,4 +54,4 @@ const ErrorInvalidTxHash = () => {
);
};
export
default
ErrorInvalidTxHash
;
export
default
App
ErrorInvalidTxHash
;
ui/shared/AppError/AppErrorUnverifiedEmail.tsx
0 → 100644
View file @
abdd2a51
import
{
Box
,
Text
,
Button
,
Heading
,
Icon
,
chakra
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
icon404
from
'
icons/error-pages/404.svg
'
;
import
useApiFetch
from
'
lib/api/useApiFetch
'
;
import
getErrorObjPayload
from
'
lib/errors/getErrorObjPayload
'
;
import
getErrorObjStatusCode
from
'
lib/errors/getErrorObjStatusCode
'
;
import
useToast
from
'
lib/hooks/useToast
'
;
interface
Props
{
className
?:
string
;
}
const
AppErrorUnverifiedEmail
=
({
className
}:
Props
)
=>
{
const
apiFetch
=
useApiFetch
();
const
toast
=
useToast
();
const
handleButtonClick
=
React
.
useCallback
(
async
()
=>
{
const
toastId
=
'
resend-email-error
'
;
try
{
await
apiFetch
(
'
email_resend
'
);
toast
({
id
:
toastId
,
position
:
'
top-right
'
,
title
:
'
Success
'
,
description
:
'
Email successfully resent.
'
,
status
:
'
success
'
,
variant
:
'
subtle
'
,
isClosable
:
true
,
});
}
catch
(
error
)
{
const
statusCode
=
getErrorObjStatusCode
(
error
);
const
payload
=
getErrorObjPayload
<
{
message
:
string
}
>
(
error
);
const
message
=
statusCode
===
429
?
payload
?.
message
:
undefined
;
!
toast
.
isActive
(
toastId
)
&&
toast
({
id
:
toastId
,
position
:
'
top-right
'
,
title
:
'
Error
'
,
description
:
message
||
'
Something went wrong. Try again later.
'
,
status
:
'
error
'
,
variant
:
'
subtle
'
,
isClosable
:
true
,
});
}
},
[
apiFetch
,
toast
]);
return
(
<
Box
className=
{
className
}
>
<
Icon
as=
{
icon404
}
width=
"200px"
height=
"auto"
/>
<
Heading
mt=
{
8
}
size=
"2xl"
fontFamily=
"body"
>
Email is not verified
</
Heading
>
<
Text
variant=
"secondary"
mt=
{
3
}
>
Please confirm your email address to use the My Account feature. A confirmation email was sent to test@gmail.com on signup.
{
`Didn't receive?`
}
</
Text
>
<
Button
mt=
{
8
}
size=
"lg"
variant=
"outline"
onClick=
{
handleButtonClick
}
>
Resend verification email
</
Button
>
</
Box
>
);
};
export
default
chakra
(
AppErrorUnverifiedEmail
);
ui/shared/Page/Page.tsx
View file @
abdd2a51
import
{
Flex
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
getError
StatusCode
from
'
lib/errors/getError
StatusCode
'
;
import
getError
CauseStatusCode
from
'
lib/errors/getErrorCause
StatusCode
'
;
import
getResourceErrorPayload
from
'
lib/errors/getResourceErrorPayload
'
;
import
useAdblockDetect
from
'
lib/hooks/useAdblockDetect
'
;
import
useGetCsrfToken
from
'
lib/hooks/useGetCsrfToken
'
;
import
AppError
from
'
ui/shared/AppError/AppError
'
;
import
AppErrorBlockConsensus
from
'
ui/shared/AppError/AppErrorBlockConsensus
'
;
import
AppErrorInvalidTxHash
from
'
ui/shared/AppError/AppErrorInvalidTxHash
'
;
import
AppErrorUnverifiedEmail
from
'
ui/shared/AppError/AppErrorUnverifiedEmail
'
;
import
ErrorBoundary
from
'
ui/shared/ErrorBoundary
'
;
import
ErrorInvalidTxHash
from
'
ui/shared/ErrorInvalidTxHash
'
;
import
PageContent
from
'
ui/shared/Page/PageContent
'
;
import
Header
from
'
ui/snippets/header/Header
'
;
import
NavigationDesktop
from
'
ui/snippets/navigation/NavigationDesktop
'
;
...
...
@@ -32,7 +33,7 @@ const Page = ({
useAdblockDetect
();
const
renderErrorScreen
=
React
.
useCallback
((
error
?:
Error
)
=>
{
const
statusCode
=
getErrorStatusCode
(
error
)
||
500
;
const
statusCode
=
getError
Cause
StatusCode
(
error
)
||
500
;
const
resourceErrorPayload
=
getResourceErrorPayload
(
error
);
const
messageInPayload
=
resourceErrorPayload
&&
'
message
'
in
resourceErrorPayload
&&
typeof
resourceErrorPayload
.
message
===
'
string
'
?
resourceErrorPayload
.
message
:
...
...
@@ -40,9 +41,14 @@ const Page = ({
const
isInvalidTxHash
=
error
?.
message
.
includes
(
'
Invalid tx hash
'
);
const
isBlockConsensus
=
messageInPayload
?.
includes
(
'
Block lost consensus
'
);
const
isUnverifiedEmail
=
statusCode
===
403
&&
messageInPayload
?.
includes
(
'
Unverified email
'
);
if
(
isInvalidTxHash
)
{
return
<
PageContent
isHomePage=
{
isHomePage
}
><
ErrorInvalidTxHash
/></
PageContent
>;
return
<
PageContent
isHomePage=
{
isHomePage
}
><
AppErrorInvalidTxHash
/></
PageContent
>;
}
if
(
isUnverifiedEmail
)
{
return
<
PageContent
isHomePage=
{
isHomePage
}
><
AppErrorUnverifiedEmail
mt=
"50px"
/></
PageContent
>;
}
if
(
isBlockConsensus
)
{
...
...
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