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
1970de13
Commit
1970de13
authored
Sep 20, 2024
by
tom
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
workflow to link wallet or email to account
parent
12e07e0e
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
212 additions
and
61 deletions
+212
-61
useFetchProfileInfo.tsx
lib/hooks/useFetchProfileInfo.tsx
+1
-0
useHasAccount.ts
lib/hooks/useHasAccount.ts
+5
-1
AuthModal.tsx
ui/snippets/auth/AuthModal.tsx
+38
-14
AuthModalScreenConnectWallet.tsx
ui/snippets/auth/screens/AuthModalScreenConnectWallet.tsx
+6
-5
AuthModalScreenEmail.tsx
ui/snippets/auth/screens/AuthModalScreenEmail.tsx
+4
-3
AuthModalScreenOtpCode.tsx
ui/snippets/auth/screens/AuthModalScreenOtpCode.tsx
+6
-5
AuthModalScreenSuccessCreatedEmail.tsx
...ppets/auth/screens/AuthModalScreenSuccessCreatedEmail.tsx
+0
-14
AuthModalScreenSuccessEmail.tsx
ui/snippets/auth/screens/AuthModalScreenSuccessEmail.tsx
+45
-0
AuthModalScreenSuccessWallet.tsx
ui/snippets/auth/screens/AuthModalScreenSuccessWallet.tsx
+48
-0
types.ts
ui/snippets/auth/types.ts
+17
-7
useLogout.tsx
ui/snippets/auth/useLogout.tsx
+10
-4
UserProfileContent.tsx
ui/snippets/user/profile/UserProfileContent.tsx
+8
-4
UserProfileDesktop.tsx
ui/snippets/user/profile/UserProfileDesktop.tsx
+12
-2
UserProfileMobile.tsx
ui/snippets/user/profile/UserProfileMobile.tsx
+12
-2
No files found.
lib/hooks/useFetchProfileInfo.tsx
View file @
1970de13
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
useApiQuery
from
'
lib/api/useApiQuery
'
;
import
*
as
cookies
from
'
lib/cookies
'
;
import
*
as
cookies
from
'
lib/cookies
'
;
// TODO @tom2drum move to auth
export
default
function
useFetchProfileInfo
()
{
export
default
function
useFetchProfileInfo
()
{
return
useApiQuery
(
'
user_info
'
,
{
return
useApiQuery
(
'
user_info
'
,
{
queryOptions
:
{
queryOptions
:
{
...
...
lib/hooks/useHasAccount.ts
View file @
1970de13
...
@@ -2,14 +2,18 @@ import config from 'configs/app';
...
@@ -2,14 +2,18 @@ import config from 'configs/app';
import
{
useAppContext
}
from
'
lib/contexts/app
'
;
import
{
useAppContext
}
from
'
lib/contexts/app
'
;
import
*
as
cookies
from
'
lib/cookies
'
;
import
*
as
cookies
from
'
lib/cookies
'
;
import
useFetchProfileInfo
from
'
./useFetchProfileInfo
'
;
// TODO @tom2drum move to auth
export
default
function
useHasAccount
()
{
export
default
function
useHasAccount
()
{
const
appProps
=
useAppContext
();
const
appProps
=
useAppContext
();
const
profileQuery
=
useFetchProfileInfo
();
if
(
!
config
.
features
.
account
.
isEnabled
)
{
if
(
!
config
.
features
.
account
.
isEnabled
)
{
return
false
;
return
false
;
}
}
const
cookiesString
=
appProps
.
cookies
;
const
cookiesString
=
appProps
.
cookies
;
const
hasAuth
=
Boolean
(
cookies
.
get
(
cookies
.
NAMES
.
API_TOKEN
,
cookiesString
));
const
hasAuth
=
Boolean
(
cookies
.
get
(
cookies
.
NAMES
.
API_TOKEN
,
cookiesString
)
||
profileQuery
.
data
);
return
hasAuth
;
return
hasAuth
;
}
}
ui/snippets/auth/AuthModal.tsx
View file @
1970de13
import
{
Modal
,
ModalBody
,
ModalCloseButton
,
ModalContent
,
ModalHeader
,
ModalOverlay
}
from
'
@chakra-ui/react
'
;
import
{
Modal
,
ModalBody
,
ModalCloseButton
,
ModalContent
,
ModalHeader
,
ModalOverlay
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
Screen
}
from
'
./types
'
;
import
type
{
Screen
,
ScreenSuccess
}
from
'
./types
'
;
import
useFetchProfileInfo
from
'
lib/hooks/useFetchProfileInfo
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
AuthModalScreenConnectWallet
from
'
./screens/AuthModalScreenConnectWallet
'
;
import
AuthModalScreenConnectWallet
from
'
./screens/AuthModalScreenConnectWallet
'
;
import
AuthModalScreenEmail
from
'
./screens/AuthModalScreenEmail
'
;
import
AuthModalScreenEmail
from
'
./screens/AuthModalScreenEmail
'
;
import
AuthModalScreenOtpCode
from
'
./screens/AuthModalScreenOtpCode
'
;
import
AuthModalScreenOtpCode
from
'
./screens/AuthModalScreenOtpCode
'
;
import
AuthModalScreenSelectMethod
from
'
./screens/AuthModalScreenSelectMethod
'
;
import
AuthModalScreenSelectMethod
from
'
./screens/AuthModalScreenSelectMethod
'
;
import
AuthModalScreenSuccess
CreatedEmail
from
'
./screens/AuthModalScreenSuccessCreated
Email
'
;
import
AuthModalScreenSuccess
Email
from
'
./screens/AuthModalScreenSuccess
Email
'
;
import
AuthModalScreenSuccess
CreatedWallet
from
'
./screens/AuthModalScreenSuccessCreated
Wallet
'
;
import
AuthModalScreenSuccess
Wallet
from
'
./screens/AuthModalScreenSuccess
Wallet
'
;
interface
Props
{
interface
Props
{
initialScreen
:
Screen
;
initialScreen
:
Screen
;
...
@@ -19,6 +20,7 @@ interface Props {
...
@@ -19,6 +20,7 @@ interface Props {
const
AuthModal
=
({
initialScreen
,
onClose
}:
Props
)
=>
{
const
AuthModal
=
({
initialScreen
,
onClose
}:
Props
)
=>
{
const
[
steps
,
setSteps
]
=
React
.
useState
<
Array
<
Screen
>>
([
initialScreen
]);
const
[
steps
,
setSteps
]
=
React
.
useState
<
Array
<
Screen
>>
([
initialScreen
]);
const
profileQuery
=
useFetchProfileInfo
();
const
onNextStep
=
React
.
useCallback
((
screen
:
Screen
)
=>
{
const
onNextStep
=
React
.
useCallback
((
screen
:
Screen
)
=>
{
setSteps
((
prev
)
=>
[
...
prev
,
screen
]);
setSteps
((
prev
)
=>
[
...
prev
,
screen
]);
...
@@ -32,19 +34,27 @@ const AuthModal = ({ initialScreen, onClose }: Props) => {
...
@@ -32,19 +34,27 @@ const AuthModal = ({ initialScreen, onClose }: Props) => {
setSteps
([
initialScreen
]);
setSteps
([
initialScreen
]);
},
[
initialScreen
]);
},
[
initialScreen
]);
const
onAuthSuccess
=
React
.
useCallback
(
async
(
screen
:
ScreenSuccess
)
=>
{
const
{
data
}
=
await
profileQuery
.
refetch
();
if
(
data
)
{
onNextStep
({
...
screen
,
profile
:
data
});
}
// TODO @tom2drum handle error case
},
[
onNextStep
,
profileQuery
]);
const
header
=
(()
=>
{
const
header
=
(()
=>
{
const
currentStep
=
steps
[
steps
.
length
-
1
];
const
currentStep
=
steps
[
steps
.
length
-
1
];
switch
(
currentStep
.
type
)
{
switch
(
currentStep
.
type
)
{
case
'
select_method
'
:
case
'
select_method
'
:
return
'
Select a way to connect
'
;
return
'
Select a way to connect
'
;
case
'
connect_wallet
'
:
case
'
connect_wallet
'
:
return
'
Continue with wallet
'
;
return
currentStep
.
isAuth
?
'
Add wallet
'
:
'
Continue with wallet
'
;
case
'
email
'
:
case
'
email
'
:
return
currentStep
.
isA
ccountExists
?
'
Add email
'
:
'
Continue with email
'
;
return
currentStep
.
isA
uth
?
'
Add email
'
:
'
Continue with email
'
;
case
'
otp_code
'
:
case
'
otp_code
'
:
return
'
Confirmation code
'
;
return
'
Confirmation code
'
;
case
'
success_
created_
email
'
:
case
'
success_email
'
:
case
'
success_
created_
wallet
'
:
case
'
success_wallet
'
:
return
'
Congrats!
'
;
return
'
Congrats!
'
;
}
}
})();
})();
...
@@ -55,15 +65,29 @@ const AuthModal = ({ initialScreen, onClose }: Props) => {
...
@@ -55,15 +65,29 @@ const AuthModal = ({ initialScreen, onClose }: Props) => {
case
'
select_method
'
:
case
'
select_method
'
:
return
<
AuthModalScreenSelectMethod
onSelectMethod=
{
onNextStep
}
/>;
return
<
AuthModalScreenSelectMethod
onSelectMethod=
{
onNextStep
}
/>;
case
'
connect_wallet
'
:
case
'
connect_wallet
'
:
return
<
AuthModalScreenConnectWallet
onSuccess=
{
on
NextStep
}
onError=
{
onReset
}
/>;
return
<
AuthModalScreenConnectWallet
onSuccess=
{
on
AuthSuccess
}
onError=
{
onReset
}
isAuth=
{
currentStep
.
isAuth
}
/>;
case
'
email
'
:
case
'
email
'
:
return
<
AuthModalScreenEmail
onSubmit=
{
onNextStep
}
/>;
return
<
AuthModalScreenEmail
onSubmit=
{
onNextStep
}
isAuth=
{
currentStep
.
isAuth
}
/>;
case
'
otp_code
'
:
case
'
otp_code
'
:
return
<
AuthModalScreenOtpCode
email=
{
currentStep
.
email
}
onSubmit=
{
onNextStep
}
/>;
return
<
AuthModalScreenOtpCode
email=
{
currentStep
.
email
}
onSuccess=
{
onAuthSuccess
}
isAuth=
{
currentStep
.
isAuth
}
/>;
case
'
success_created_email
'
:
case
'
success_email
'
:
return
<
AuthModalScreenSuccessCreatedEmail
/>;
return
(
case
'
success_created_wallet
'
:
<
AuthModalScreenSuccessEmail
return
<
AuthModalScreenSuccessCreatedWallet
address=
{
currentStep
.
address
}
onAddEmail=
{
onNextStep
}
/>;
email=
{
currentStep
.
email
}
onConnectWallet=
{
onNextStep
}
isAuth=
{
currentStep
.
isAuth
}
profile=
{
currentStep
.
profile
}
/>
);
case
'
success_wallet
'
:
return
(
<
AuthModalScreenSuccessWallet
address=
{
currentStep
.
address
}
onAddEmail=
{
onNextStep
}
isAuth=
{
currentStep
.
isAuth
}
profile=
{
currentStep
.
profile
}
/>
);
}
}
})();
})();
...
...
ui/snippets/auth/screens/AuthModalScreenConnectWallet.tsx
View file @
1970de13
import
{
Center
,
Spinner
}
from
'
@chakra-ui/react
'
;
import
{
Center
,
Spinner
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
Screen
}
from
'
../types
'
;
import
type
{
Screen
Success
}
from
'
../types
'
;
import
useSignInWithWallet
from
'
../useSignInWithWallet
'
;
import
useSignInWithWallet
from
'
../useSignInWithWallet
'
;
interface
Props
{
interface
Props
{
onSuccess
:
(
screen
:
Screen
)
=>
void
;
onSuccess
:
(
screen
:
Screen
Success
)
=>
void
;
onError
:
()
=>
void
;
onError
:
()
=>
void
;
isAuth
?:
boolean
;
}
}
const
AuthModalScreenConnectWallet
=
({
onSuccess
,
onError
}:
Props
)
=>
{
const
AuthModalScreenConnectWallet
=
({
onSuccess
,
onError
,
isAuth
}:
Props
)
=>
{
const
isStartedRef
=
React
.
useRef
(
false
);
const
isStartedRef
=
React
.
useRef
(
false
);
const
handleSignInSuccess
=
React
.
useCallback
(({
address
}:
{
address
:
string
})
=>
{
const
handleSignInSuccess
=
React
.
useCallback
(({
address
}:
{
address
:
string
})
=>
{
onSuccess
({
type
:
'
success_
created_wallet
'
,
address
});
onSuccess
({
type
:
'
success_
wallet
'
,
address
,
isAuth
});
},
[
onSuccess
]);
},
[
onSuccess
,
isAuth
]);
const
handleSignInError
=
React
.
useCallback
(()
=>
{
const
handleSignInError
=
React
.
useCallback
(()
=>
{
onError
();
onError
();
...
...
ui/snippets/auth/screens/AuthModalScreenEmail.tsx
View file @
1970de13
...
@@ -14,9 +14,10 @@ import AuthModalFieldEmail from '../fields/AuthModalFieldEmail';
...
@@ -14,9 +14,10 @@ import AuthModalFieldEmail from '../fields/AuthModalFieldEmail';
interface
Props
{
interface
Props
{
onSubmit
:
(
screen
:
Screen
)
=>
void
;
onSubmit
:
(
screen
:
Screen
)
=>
void
;
isAuth
?:
boolean
;
}
}
const
AuthModalScreenEmail
=
({
onSubmit
}:
Props
)
=>
{
const
AuthModalScreenEmail
=
({
onSubmit
,
isAuth
}:
Props
)
=>
{
const
apiFetch
=
useApiFetch
();
const
apiFetch
=
useApiFetch
();
const
toast
=
useToast
();
const
toast
=
useToast
();
...
@@ -39,7 +40,7 @@ const AuthModalScreenEmail = ({ onSubmit }: Props) => {
...
@@ -39,7 +40,7 @@ const AuthModalScreenEmail = ({ onSubmit }: Props) => {
},
},
})
})
.
then
(()
=>
{
.
then
(()
=>
{
onSubmit
({
type
:
'
otp_code
'
,
email
:
formData
.
email
});
onSubmit
({
type
:
'
otp_code
'
,
email
:
formData
.
email
,
isAuth
});
})
})
.
catch
((
error
)
=>
{
.
catch
((
error
)
=>
{
toast
({
toast
({
...
@@ -48,7 +49,7 @@ const AuthModalScreenEmail = ({ onSubmit }: Props) => {
...
@@ -48,7 +49,7 @@ const AuthModalScreenEmail = ({ onSubmit }: Props) => {
description
:
getErrorMessage
(
error
)
||
'
Something went wrong
'
,
description
:
getErrorMessage
(
error
)
||
'
Something went wrong
'
,
});
});
});
});
},
[
apiFetch
,
onSubmit
,
toast
]);
},
[
apiFetch
,
onSubmit
,
toast
,
isAuth
]);
return
(
return
(
<
FormProvider
{
...
formApi
}
>
<
FormProvider
{
...
formApi
}
>
...
...
ui/snippets/auth/screens/AuthModalScreenOtpCode.tsx
View file @
1970de13
...
@@ -3,7 +3,7 @@ import React from 'react';
...
@@ -3,7 +3,7 @@ import React from 'react';
import
type
{
SubmitHandler
}
from
'
react-hook-form
'
;
import
type
{
SubmitHandler
}
from
'
react-hook-form
'
;
import
{
FormProvider
,
useForm
}
from
'
react-hook-form
'
;
import
{
FormProvider
,
useForm
}
from
'
react-hook-form
'
;
import
type
{
OtpCodeFormFields
,
Screen
}
from
'
../types
'
;
import
type
{
OtpCodeFormFields
,
Screen
Success
}
from
'
../types
'
;
import
useApiFetch
from
'
lib/api/useApiFetch
'
;
import
useApiFetch
from
'
lib/api/useApiFetch
'
;
import
getErrorMessage
from
'
lib/errors/getErrorMessage
'
;
import
getErrorMessage
from
'
lib/errors/getErrorMessage
'
;
...
@@ -14,10 +14,11 @@ import AuthModalFieldOtpCode from '../fields/AuthModalFieldOtpCode';
...
@@ -14,10 +14,11 @@ import AuthModalFieldOtpCode from '../fields/AuthModalFieldOtpCode';
interface
Props
{
interface
Props
{
email
:
string
;
email
:
string
;
onSubmit
:
(
screen
:
Screen
)
=>
void
;
onSuccess
:
(
screen
:
ScreenSuccess
)
=>
void
;
isAuth
?:
boolean
;
}
}
const
AuthModalScreenOtpCode
=
({
email
,
onSu
bmit
}:
Props
)
=>
{
const
AuthModalScreenOtpCode
=
({
email
,
onSu
ccess
,
isAuth
}:
Props
)
=>
{
const
apiFetch
=
useApiFetch
();
const
apiFetch
=
useApiFetch
();
const
toast
=
useToast
();
const
toast
=
useToast
();
...
@@ -40,7 +41,7 @@ const AuthModalScreenOtpCode = ({ email, onSubmit }: Props) => {
...
@@ -40,7 +41,7 @@ const AuthModalScreenOtpCode = ({ email, onSubmit }: Props) => {
},
},
})
})
.
then
(()
=>
{
.
then
(()
=>
{
onSu
bmit
({
type
:
'
success_created_email
'
});
onSu
ccess
({
type
:
'
success_email
'
,
email
,
isAuth
});
})
})
.
catch
((
error
)
=>
{
.
catch
((
error
)
=>
{
// TODO @tom2drum handle incorrect code error
// TODO @tom2drum handle incorrect code error
...
@@ -50,7 +51,7 @@ const AuthModalScreenOtpCode = ({ email, onSubmit }: Props) => {
...
@@ -50,7 +51,7 @@ const AuthModalScreenOtpCode = ({ email, onSubmit }: Props) => {
description
:
getErrorMessage
(
error
)
||
'
Something went wrong
'
,
description
:
getErrorMessage
(
error
)
||
'
Something went wrong
'
,
});
});
});
});
},
[
apiFetch
,
email
,
onSu
bmit
,
toast
]);
},
[
apiFetch
,
email
,
onSu
ccess
,
toast
,
isAuth
]);
const
handleResendCodeClick
=
React
.
useCallback
(()
=>
{
const
handleResendCodeClick
=
React
.
useCallback
(()
=>
{
return
apiFetch
(
'
auth_send_otp
'
,
{
return
apiFetch
(
'
auth_send_otp
'
,
{
...
...
ui/snippets/auth/screens/AuthModalScreenSuccessCreatedEmail.tsx
deleted
100644 → 0
View file @
12e07e0e
import
{
Box
,
Text
,
Button
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
const
AuthModalScreenSuccessCreatedEmail
=
()
=>
{
return
(
<
Box
>
<
Text
>
Your account was successfully created!
</
Text
>
<
Text
mt=
{
6
}
>
Connect a web3 wallet to safely interact with smart contracts and dapps inside Blockscout.
</
Text
>
<
Button
mt=
{
6
}
>
Connect wallet
</
Button
>
</
Box
>
);
};
export
default
React
.
memo
(
AuthModalScreenSuccessCreatedEmail
);
ui/snippets/auth/screens/AuthModalScreenSuccessEmail.tsx
0 → 100644
View file @
1970de13
import
{
chakra
,
Box
,
Text
,
Button
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
Screen
}
from
'
../types
'
;
import
type
{
UserInfo
}
from
'
types/api/account
'
;
interface
Props
{
email
:
string
;
onConnectWallet
:
(
screen
:
Screen
)
=>
void
;
isAuth
?:
boolean
;
profile
:
UserInfo
|
undefined
;
}
const
AuthModalScreenSuccessEmail
=
({
email
,
onConnectWallet
,
isAuth
,
profile
}:
Props
)
=>
{
const
handleConnectWalletClick
=
React
.
useCallback
(()
=>
{
onConnectWallet
({
type
:
'
connect_wallet
'
,
isAuth
:
true
});
},
[
onConnectWallet
]);
if
(
isAuth
)
{
return
(
<
Text
>
Your account was linked to
{
'
'
}
<
chakra
.
span
fontWeight=
"700"
>
{
email
}
</
chakra
.
span
>
{
'
'
}
email. Use for the next login.
</
Text
>
);
}
return
(
<
Box
>
<
Text
>
<
chakra
.
span
fontWeight=
"700"
>
{
email
}
</
chakra
.
span
>
{
'
'
}
email has been successfully used to log in to your Blockscout account.
</
Text
>
{
!
profile
?.
address_hash
&&
(
<>
<
Text
mt=
{
6
}
>
Add your web3 wallet to safely interact with smart contracts and dapps inside Blockscout.
</
Text
>
<
Button
mt=
{
6
}
onClick=
{
handleConnectWalletClick
}
>
Connect wallet
</
Button
>
</>
)
}
</
Box
>
);
};
export
default
React
.
memo
(
AuthModalScreenSuccessEmail
);
ui/snippets/auth/screens/AuthModalScreenSuccess
Created
Wallet.tsx
→
ui/snippets/auth/screens/AuthModalScreenSuccessWallet.tsx
View file @
1970de13
...
@@ -2,30 +2,47 @@ import { chakra, Box, Text, Button } from '@chakra-ui/react';
...
@@ -2,30 +2,47 @@ import { chakra, Box, Text, Button } from '@chakra-ui/react';
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
Screen
}
from
'
../types
'
;
import
type
{
Screen
}
from
'
../types
'
;
import
type
{
UserInfo
}
from
'
types/api/account
'
;
import
shortenString
from
'
lib/shortenString
'
;
import
shortenString
from
'
lib/shortenString
'
;
interface
Props
{
interface
Props
{
address
:
string
;
address
:
string
;
onAddEmail
:
(
screen
:
Screen
)
=>
void
;
onAddEmail
:
(
screen
:
Screen
)
=>
void
;
isAuth
?:
boolean
;
profile
:
UserInfo
|
undefined
;
}
}
const
AuthModalScreenSuccess
CreatedWallet
=
({
address
,
onAddEmail
}:
Props
)
=>
{
const
AuthModalScreenSuccess
Wallet
=
({
address
,
onAddEmail
,
isAuth
,
profile
}:
Props
)
=>
{
const
handleAddEmailClick
=
React
.
useCallback
(()
=>
{
const
handleAddEmailClick
=
React
.
useCallback
(()
=>
{
onAddEmail
({
type
:
'
email
'
,
isA
ccountExists
:
true
});
onAddEmail
({
type
:
'
email
'
,
isA
uth
:
true
});
},
[
onAddEmail
]);
},
[
onAddEmail
]);
return
(
if
(
isAuth
)
{
<
Box
>
return
(
<
Text
>
<
Text
>
Your account was linked to
{
'
'
}
Your account was linked to
{
'
'
}
<
chakra
.
span
fontWeight=
"700"
>
{
shortenString
(
address
)
}
</
chakra
.
span
>
{
'
'
}
<
chakra
.
span
fontWeight=
"700"
>
{
shortenString
(
address
)
}
</
chakra
.
span
>
{
'
'
}
wallet. Use for the next login.
wallet. Use for the next login.
</
Text
>
</
Text
>
<
Text
mt=
{
6
}
>
Add your email to receive notifications about addresses in your watch list.
</
Text
>
);
<
Button
mt=
{
6
}
onClick=
{
handleAddEmailClick
}
>
Add email
</
Button
>
}
return
(
<
Box
>
<
Text
>
Wallet
{
'
'
}
<
chakra
.
span
fontWeight=
"700"
>
{
shortenString
(
address
)
}
</
chakra
.
span
>
{
'
'
}
has been successfully used to log in to your Blockscout account.
</
Text
>
{
!
profile
?.
email
&&
(
<>
<
Text
mt=
{
6
}
>
Add your email to receive notifications about addresses in your watch list.
</
Text
>
<
Button
mt=
{
6
}
onClick=
{
handleAddEmailClick
}
>
Add email
</
Button
>
</>
)
}
</
Box
>
</
Box
>
);
);
};
};
export
default
React
.
memo
(
AuthModalScreenSuccess
Created
Wallet
);
export
default
React
.
memo
(
AuthModalScreenSuccessWallet
);
ui/snippets/auth/types.ts
View file @
1970de13
import
type
{
UserInfo
}
from
'
types/api/account
'
;
export
type
ScreenSuccess
=
{
type
:
'
success_email
'
;
email
:
string
;
profile
?:
UserInfo
;
isAuth
?:
boolean
;
}
|
{
type
:
'
success_wallet
'
;
address
:
string
;
profile
?:
UserInfo
;
isAuth
?:
boolean
;
}
export
type
Screen
=
{
export
type
Screen
=
{
type
:
'
select_method
'
;
type
:
'
select_method
'
;
}
|
{
}
|
{
type
:
'
connect_wallet
'
;
type
:
'
connect_wallet
'
;
isAuth
?:
boolean
;
}
|
{
}
|
{
type
:
'
email
'
;
type
:
'
email
'
;
isA
ccountExists
?:
boolean
;
isA
uth
?:
boolean
;
}
|
{
}
|
{
type
:
'
otp_code
'
;
type
:
'
otp_code
'
;
email
:
string
;
email
:
string
;
}
|
{
isAuth
?:
boolean
;
type
:
'
success_created_email
'
;
}
|
ScreenSuccess
;
}
|
{
type
:
'
success_created_wallet
'
;
address
:
string
;
}
export
interface
EmailFormFields
{
export
interface
EmailFormFields
{
email
:
string
;
email
:
string
;
...
...
ui/snippets/auth/useLogout.tsx
View file @
1970de13
import
{
useQueryClient
}
from
'
@tanstack/react-query
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
Route
}
from
'
nextjs-routes
'
;
import
type
{
Route
}
from
'
nextjs-routes
'
;
import
{
getResourceKey
}
from
'
lib/api/useApiQuery
'
;
import
*
as
cookies
from
'
lib/cookies
'
;
import
*
as
cookies
from
'
lib/cookies
'
;
const
PROTECTED_ROUTES
:
Array
<
Route
[
'
pathname
'
]
>
=
[
const
PROTECTED_ROUTES
:
Array
<
Route
[
'
pathname
'
]
>
=
[
...
@@ -17,17 +19,21 @@ const PROTECTED_ROUTES: Array<Route['pathname']> = [
...
@@ -17,17 +19,21 @@ const PROTECTED_ROUTES: Array<Route['pathname']> = [
export
default
function
useLogout
()
{
export
default
function
useLogout
()
{
const
router
=
useRouter
();
const
router
=
useRouter
();
const
queryClient
=
useQueryClient
();
return
React
.
useCallback
(
async
()
=>
{
return
React
.
useCallback
(
async
()
=>
{
cookies
.
remove
(
cookies
.
NAMES
.
API_TOKEN
);
cookies
.
remove
(
cookies
.
NAMES
.
API_TOKEN
);
queryClient
.
resetQueries
({
queryKey
:
getResourceKey
(
'
user_info
'
),
exact
:
true
,
});
if
(
if
(
PROTECTED_ROUTES
.
includes
(
router
.
pathname
)
||
PROTECTED_ROUTES
.
includes
(
router
.
pathname
)
||
(
router
.
pathname
===
'
/txs
'
&&
router
.
query
.
tab
===
'
watchlist
'
)
(
router
.
pathname
===
'
/txs
'
&&
router
.
query
.
tab
===
'
watchlist
'
)
)
{
)
{
window
.
location
.
assign
(
'
/
'
);
router
.
push
({
pathname
:
'
/
'
},
undefined
,
{
shallow
:
true
});
}
else
{
window
.
location
.
reload
();
}
}
},
[
router
]);
},
[
queryClient
,
router
]);
}
}
ui/snippets/user/profile/UserProfileContent.tsx
View file @
1970de13
import
{
Box
,
Divider
,
Flex
,
Text
,
VStack
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Divider
,
Flex
,
Link
,
Text
,
VStack
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
NavLink
}
from
'
./types
'
;
import
type
{
NavLink
}
from
'
./types
'
;
...
@@ -45,10 +45,11 @@ const navLinks: Array<NavLink> = [
...
@@ -45,10 +45,11 @@ const navLinks: Array<NavLink> = [
interface
Props
{
interface
Props
{
data
:
UserInfo
;
data
:
UserInfo
;
onClose
?:
()
=>
void
;
onClose
:
()
=>
void
;
onAddEmail
:
()
=>
void
;
}
}
const
UserProfileContent
=
({
data
,
onClose
}:
Props
)
=>
{
const
UserProfileContent
=
({
data
,
onClose
,
onAddEmail
}:
Props
)
=>
{
const
{
isAutoConnectDisabled
}
=
useMarketplaceContext
();
const
{
isAutoConnectDisabled
}
=
useMarketplaceContext
();
const
logout
=
useLogout
();
const
logout
=
useLogout
();
...
@@ -63,7 +64,10 @@ const UserProfileContent = ({ data, onClose }: Props) => {
...
@@ -63,7 +64,10 @@ const UserProfileContent = ({ data, onClose }: Props) => {
icon=
"profile"
icon=
"profile"
onClick=
{
onClose
}
onClick=
{
onClose
}
/>
/>
{
data
?.
email
&&
<
Text
variant=
"secondary"
fontSize=
"sm"
>
{
getUserHandle
(
data
.
email
)
}
</
Text
>
}
{
data
?.
email
?
<
Text
variant=
"secondary"
fontSize=
"sm"
>
{
getUserHandle
(
data
.
email
)
}
</
Text
>
:
<
Link
onClick=
{
onAddEmail
}
color=
"text_secondary"
fontSize=
"sm"
_hover=
{
{
color
:
'
link_hovered
'
,
textDecoration
:
'
none
'
}
}
>
Add email
</
Link
>
}
</
Flex
>
</
Flex
>
{
config
.
features
.
blockchainInteraction
.
isEnabled
?
<
UserProfileContentWallet
onClose=
{
onClose
}
/>
:
<
Divider
/>
}
{
config
.
features
.
blockchainInteraction
.
isEnabled
?
<
UserProfileContentWallet
onClose=
{
onClose
}
/>
:
<
Divider
/>
}
...
...
ui/snippets/user/profile/UserProfileDesktop.tsx
View file @
1970de13
...
@@ -2,6 +2,8 @@ import { PopoverBody, PopoverContent, PopoverTrigger, useDisclosure, type Button
...
@@ -2,6 +2,8 @@ import { PopoverBody, PopoverContent, PopoverTrigger, useDisclosure, type Button
import
{
useRouter
}
from
'
next/router
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
Screen
}
from
'
ui/snippets/auth/types
'
;
import
config
from
'
configs/app
'
;
import
config
from
'
configs/app
'
;
import
useFetchProfileInfo
from
'
lib/hooks/useFetchProfileInfo
'
;
import
useFetchProfileInfo
from
'
lib/hooks/useFetchProfileInfo
'
;
import
Popover
from
'
ui/shared/chakra/Popover
'
;
import
Popover
from
'
ui/shared/chakra/Popover
'
;
...
@@ -17,6 +19,9 @@ interface Props {
...
@@ -17,6 +19,9 @@ interface Props {
}
}
const
UserProfileDesktop
=
({
buttonSize
,
buttonVariant
=
'
header
'
}:
Props
)
=>
{
const
UserProfileDesktop
=
({
buttonSize
,
buttonVariant
=
'
header
'
}:
Props
)
=>
{
const
[
authInitialScreen
,
setAuthInitialScreen
]
=
React
.
useState
<
Screen
>
({
type
:
config
.
features
.
blockchainInteraction
.
isEnabled
?
'
select_method
'
:
'
email
'
,
});
const
router
=
useRouter
();
const
router
=
useRouter
();
const
authModal
=
useDisclosure
();
const
authModal
=
useDisclosure
();
...
@@ -39,6 +44,11 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) =>
...
@@ -39,6 +44,11 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) =>
authModal
.
onOpen
();
authModal
.
onOpen
();
},
[
profileQuery
.
data
,
router
.
pathname
,
authModal
,
profileMenu
,
signInWithWallet
]);
},
[
profileQuery
.
data
,
router
.
pathname
,
authModal
,
profileMenu
,
signInWithWallet
]);
const
handleAddEmailClick
=
React
.
useCallback
(()
=>
{
setAuthInitialScreen
({
type
:
'
email
'
,
isAuth
:
true
});
authModal
.
onOpen
();
},
[
authModal
]);
return
(
return
(
<>
<>
<
Popover
openDelay=
{
300
}
placement=
"bottom-end"
isLazy
isOpen=
{
profileMenu
.
isOpen
}
onClose=
{
profileMenu
.
onClose
}
>
<
Popover
openDelay=
{
300
}
placement=
"bottom-end"
isLazy
isOpen=
{
profileMenu
.
isOpen
}
onClose=
{
profileMenu
.
onClose
}
>
...
@@ -54,7 +64,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) =>
...
@@ -54,7 +64,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) =>
{
profileQuery
.
data
&&
(
{
profileQuery
.
data
&&
(
<
PopoverContent
maxW=
"280px"
minW=
"220px"
w=
"min-content"
>
<
PopoverContent
maxW=
"280px"
minW=
"220px"
w=
"min-content"
>
<
PopoverBody
>
<
PopoverBody
>
<
UserProfileContent
data=
{
profileQuery
.
data
}
onClose=
{
profileMenu
.
onClose
}
/>
<
UserProfileContent
data=
{
profileQuery
.
data
}
onClose=
{
profileMenu
.
onClose
}
onAddEmail=
{
handleAddEmailClick
}
/>
</
PopoverBody
>
</
PopoverBody
>
</
PopoverContent
>
</
PopoverContent
>
)
}
)
}
...
@@ -62,7 +72,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) =>
...
@@ -62,7 +72,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) =>
{
authModal
.
isOpen
&&
(
{
authModal
.
isOpen
&&
(
<
AuthModal
<
AuthModal
onClose=
{
authModal
.
onClose
}
onClose=
{
authModal
.
onClose
}
initialScreen=
{
{
type
:
config
.
features
.
blockchainInteraction
.
isEnabled
?
'
select_method
'
:
'
email
'
}
}
initialScreen=
{
authInitialScreen
}
/>
/>
)
}
)
}
</>
</>
...
...
ui/snippets/user/profile/UserProfileMobile.tsx
View file @
1970de13
...
@@ -2,6 +2,8 @@ import { Drawer, DrawerBody, DrawerContent, DrawerOverlay, useDisclosure } from
...
@@ -2,6 +2,8 @@ import { Drawer, DrawerBody, DrawerContent, DrawerOverlay, useDisclosure } from
import
{
useRouter
}
from
'
next/router
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
Screen
}
from
'
ui/snippets/auth/types
'
;
import
config
from
'
configs/app
'
;
import
config
from
'
configs/app
'
;
import
useFetchProfileInfo
from
'
lib/hooks/useFetchProfileInfo
'
;
import
useFetchProfileInfo
from
'
lib/hooks/useFetchProfileInfo
'
;
import
AuthModal
from
'
ui/snippets/auth/AuthModal
'
;
import
AuthModal
from
'
ui/snippets/auth/AuthModal
'
;
...
@@ -11,6 +13,9 @@ import UserProfileButton from './UserProfileButton';
...
@@ -11,6 +13,9 @@ import UserProfileButton from './UserProfileButton';
import
UserProfileContent
from
'
./UserProfileContent
'
;
import
UserProfileContent
from
'
./UserProfileContent
'
;
const
UserProfileMobile
=
()
=>
{
const
UserProfileMobile
=
()
=>
{
const
[
authInitialScreen
,
setAuthInitialScreen
]
=
React
.
useState
<
Screen
>
({
type
:
config
.
features
.
blockchainInteraction
.
isEnabled
?
'
select_method
'
:
'
email
'
,
});
const
router
=
useRouter
();
const
router
=
useRouter
();
const
authModal
=
useDisclosure
();
const
authModal
=
useDisclosure
();
...
@@ -33,6 +38,11 @@ const UserProfileMobile = () => {
...
@@ -33,6 +38,11 @@ const UserProfileMobile = () => {
authModal
.
onOpen
();
authModal
.
onOpen
();
},
[
profileQuery
.
data
,
router
.
pathname
,
authModal
,
profileMenu
,
signInWithWallet
]);
},
[
profileQuery
.
data
,
router
.
pathname
,
authModal
,
profileMenu
,
signInWithWallet
]);
const
handleAddEmailClick
=
React
.
useCallback
(()
=>
{
setAuthInitialScreen
({
type
:
'
email
'
,
isAuth
:
true
});
authModal
.
onOpen
();
},
[
authModal
]);
return
(
return
(
<>
<>
<
UserProfileButton
<
UserProfileButton
...
@@ -50,7 +60,7 @@ const UserProfileMobile = () => {
...
@@ -50,7 +60,7 @@ const UserProfileMobile = () => {
<
DrawerOverlay
/>
<
DrawerOverlay
/>
<
DrawerContent
maxWidth=
"300px"
>
<
DrawerContent
maxWidth=
"300px"
>
<
DrawerBody
p=
{
6
}
>
<
DrawerBody
p=
{
6
}
>
<
UserProfileContent
data=
{
profileQuery
.
data
}
onClose=
{
profileMenu
.
onClose
}
/>
<
UserProfileContent
data=
{
profileQuery
.
data
}
onClose=
{
profileMenu
.
onClose
}
onAddEmail=
{
handleAddEmailClick
}
/>
</
DrawerBody
>
</
DrawerBody
>
</
DrawerContent
>
</
DrawerContent
>
</
Drawer
>
</
Drawer
>
...
@@ -58,7 +68,7 @@ const UserProfileMobile = () => {
...
@@ -58,7 +68,7 @@ const UserProfileMobile = () => {
{
authModal
.
isOpen
&&
(
{
authModal
.
isOpen
&&
(
<
AuthModal
<
AuthModal
onClose=
{
authModal
.
onClose
}
onClose=
{
authModal
.
onClose
}
initialScreen=
{
{
type
:
config
.
features
.
blockchainInteraction
.
isEnabled
?
'
select_method
'
:
'
email
'
}
}
initialScreen=
{
authInitialScreen
}
/>
/>
)
}
)
}
</>
</>
...
...
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