Skip to content

Commit

Permalink
feat: change owner ui (#8345)
Browse files Browse the repository at this point in the history
* feat: change owner ui

* feat: logout tip

* refactor: change owner implement

* fix: bugfix

* refactor: add comment

* refactor: code style

* fix: read owner from options

* chore: lock file

* refactor: remove transfer & changeOwner method

* fix: lint error

* refactor: handler priority

* fix: add owner and identifier in request db

* fix: make smartpay stable

* feat: open change owner window

* chore: ui bugfix

* refactor: remove lopgs

Co-authored-by: guanbinrui <guanbinrui@dimension.im>
Co-authored-by: guanbinrui <52657989+guanbinrui@users.noreply.github.com>
  • Loading branch information
3 people committed Jan 4, 2023
1 parent cdf8458 commit 7d1bb9d
Show file tree
Hide file tree
Showing 35 changed files with 693 additions and 313 deletions.
1 change: 1 addition & 0 deletions packages/dashboard/src/locales/en-US.json
Expand Up @@ -229,6 +229,7 @@
"personas_disconnect_raw": "Disconnect",
"personas_disconnect_warning": "Are you sure you want to delete persona verification? Your mask friends can no longer send decrypted message to you by this persona or check your Web 3 products related with this persona.",
"personas_logout_warning": "After logging out, your associated social accounts can no longer decrypt past encrypted messages. If you need to reuse your account, you can use your private key for recovery.",
"personas_logout_manage_wallet_warning": "Please note: This Persona {{persona}} is the management account of SmartPay wallet {{addresses}}. You cannot use SmartPay wallet to interact with blockchain after logging out persona.",
"personas_add": "Add",
"personas_upload_avatar": "Upload an avatar",
"personas_rename": "Rename",
Expand Down
@@ -1,23 +1,27 @@
import { Icons } from '@masknet/icons'
import { DashboardRoutes, PersonaIdentifier } from '@masknet/shared-base'
import { MaskDialog } from '@masknet/theme'
import { useWallets } from '@masknet/web3-hooks-base'
import { formatEthereumAddress } from '@masknet/web3-shared-evm'
import { LoadingButton } from '@mui/lab'
import { Box, Button, DialogActions, DialogContent, Typography } from '@mui/material'
import { memo, useCallback } from 'react'
import { memo, useCallback, useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
import { Services } from '../../../../API.js'
import { useDashboardI18N } from '../../../../locales/index.js'
import { PersonaContext } from '../../hooks/usePersonaContext.js'

export interface LogoutPersonaDialogProps {
open: boolean
nickname?: string
onClose: () => void
identifier: PersonaIdentifier
}

export const LogoutPersonaDialog = memo<LogoutPersonaDialogProps>(({ open, onClose, identifier }) => {
export const LogoutPersonaDialog = memo<LogoutPersonaDialogProps>(({ open, onClose, identifier, nickname }) => {
const t = useDashboardI18N()
const navigate = useNavigate()
const wallets = useWallets()
const { changeCurrentPersona } = PersonaContext.useContainer()
const handleLogout = useCallback(async () => {
await Services.Identity.logoutPersona(identifier)
Expand All @@ -32,6 +36,10 @@ export const LogoutPersonaDialog = memo<LogoutPersonaDialogProps>(({ open, onClo
}
}, [identifier, onClose])

const manageWallets = useMemo(() => {
return wallets.filter((x) => x.identifier?.toText() === identifier.toText())
}, [wallets, identifier])

return (
<MaskDialog open={open} title={t.personas_logout()} onClose={onClose} maxWidth="xs">
<DialogContent>
Expand All @@ -43,6 +51,14 @@ export const LogoutPersonaDialog = memo<LogoutPersonaDialogProps>(({ open, onClo
<Typography color="error" variant="body2" fontSize={13}>
{t.personas_logout_warning()}
</Typography>
{manageWallets.length ? (
<Typography color="error" variant="body2" fontSize={13}>
{t.personas_logout_manage_wallet_warning({
persona: nickname ?? '',
addresses: manageWallets.map((x) => formatEthereumAddress(x.address, 4)).join(','),
})}
</Typography>
) : null}
</DialogContent>
<DialogActions>
<Button color="secondary" onClick={onClose} sx={{ minWidth: 150 }}>
Expand Down
Expand Up @@ -230,6 +230,7 @@ export const PersonaRowCardUI = memo<PersonaRowCardUIProps>((props) => {
/>
)}
<LogoutPersonaDialog
nickname={nickname}
open={logoutDialogOpen}
identifier={identifier}
onClose={() => setLogoutDialogOpen(false)}
Expand Down
2 changes: 1 addition & 1 deletion packages/icons/general/PopupLink.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions packages/mask/shared-ui/locales/en-US.json
Expand Up @@ -980,6 +980,11 @@
"popups_wallet_no_transactions": "You have no transactions",
"popups_wallet_more_expand": "Show tokens with value less than $1",
"popups_wallet_more_collapse": "Hide tokens with value less than $1",
"popups_wallet_change_owner": "Change Owner",
"popups_wallet_current_managed_accounts": "Current Managed Accounts",
"popups_wallet_set_a_new_manage_account": "Set a New Manage Account",
"popups_wallet_no_encrypted_placeholder": "No encrypted friends,You can try searching.",
"popups_wallet_wallets": "Wallets",
"popups_missing_parameter_caption": "Please close this page.",
"popups_persona_connect_to": "Connect to {{type}}",
"popups_persona_to_be_verified": "To be verified",
Expand All @@ -992,6 +997,7 @@
"popups_twitter_id": "Twitter ID",
"popups_persona_logout": "Log out",
"popups_persona_disconnect_tip": "After logging out, your associated social accounts can no longer decrypt past encrypted messages. If you need to reuse your account, you can recover your account with your identity, private key, local or cloud backup.",
"popups_persona_disconnect_manage_wallet_warning": "Please note: This Persona {{persona}} is the management account of SmartPay wallet {{addresses}}. You cannot use SmartPay wallet to interact with blockchain after logging out persona.",
"popups_persona_persona_name_exists": "The persona name already exists.",
"popups_persona_sign_request_title": "Signature request",
"popups_persona_sign_request_message": "message",
Expand Down
@@ -0,0 +1,38 @@
import { Icons } from '@masknet/icons'
import { makeStyles } from '@masknet/theme'
import { Radio, radioClasses, RadioProps } from '@mui/material'
import { forwardRef, memo } from 'react'

const useStyles = makeStyles()((theme) => ({
root: {
'&:hover': {
backgroundColor: 'transparent',
},
color: theme.palette.maskColor.secondaryLine,
[`&.${radioClasses.checked} svg`]: {
filter: 'drop-shadow(0px 4px 10px rgba(28, 104, 243, 0.2))',
},
[`&.${radioClasses.disabled} svg`]: {
color: theme.palette.maskColor.secondaryLine,
'& circle': {
fill: `${theme.palette.maskColor.bg} !important`,
},
},
},
}))

export const StyledRadio = memo(
forwardRef<{}, RadioProps>((props, ref) => {
const { classes } = useStyles()
return (
<Radio
{...props}
classes={{ root: classes.root }}
inputRef={ref}
checkedIcon={<Icons.RadioButtonChecked size={20} />}
icon={<Icons.RadioButtonUnChecked size={20} />}
disableRipple
/>
)
}),
)
151 changes: 94 additions & 57 deletions packages/mask/src/extension/popups/pages/Personas/Logout/index.tsx
Expand Up @@ -8,9 +8,12 @@ import { PersonaContext } from '../hooks/usePersonaContext.js'
import Services from '../../../../service.js'
import { LoadingButton } from '@mui/lab'
import { useNavigate } from 'react-router-dom'
import { PopupRoutes, formatPersonaFingerprint, type PersonaInformation } from '@masknet/shared-base'
import { PopupRoutes, formatPersonaFingerprint, type PersonaInformation, NetworkPluginID } from '@masknet/shared-base'
import { PasswordField } from '../../../components/PasswordField/index.js'
import { useTitle } from '../../../hook/useTitle.js'
import { useWallets } from '@masknet/web3-hooks-base'
import type { Wallet } from '@masknet/web3-shared-base'
import { formatEthereumAddress } from '@masknet/web3-shared-evm'

const useStyles = makeStyles()((theme) => ({
content: {
Expand Down Expand Up @@ -73,12 +76,14 @@ const useStyles = makeStyles()((theme) => ({
padding: '8px 16px',
margin: '20px 0',
width: '100%',
marginBottom: 12,
},
}))

const Logout = memo(() => {
const { selectedPersona } = PersonaContext.useContainer()
const navigate = useNavigate()
const wallets = useWallets(NetworkPluginID.PLUGIN_EVM)
const backupPassword = useMemo(() => {
try {
const password = localStorage.getItem('backupPassword')
Expand All @@ -99,8 +104,14 @@ const Logout = memo(() => {
}
navigate(PopupRoutes.Personas, { replace: true })
}, [selectedPersona, history])

const manageWallets = useMemo(() => {
return wallets.filter((x) => x.identifier?.toText() === selectedPersona?.identifier.toText())
}, [wallets, selectedPersona])

return (
<LogoutUI
manageWallets={manageWallets}
selectedPersona={selectedPersona}
backupPassword={backupPassword ?? ''}
loading={loading}
Expand All @@ -111,76 +122,102 @@ const Logout = memo(() => {
})

export interface LogoutUIProps {
manageWallets: Wallet[]
selectedPersona?: PersonaInformation
backupPassword: string
loading: boolean
onCancel: () => void
onLogout: () => void
}

export const LogoutUI = memo<LogoutUIProps>(({ backupPassword, loading, onLogout, onCancel, selectedPersona }) => {
const { t } = useI18N()
const { classes } = useStyles()
const [password, setPassword] = useState('')
const [error, setError] = useState(false)
export const LogoutUI = memo<LogoutUIProps>(
({ backupPassword, loading, onLogout, onCancel, selectedPersona, manageWallets }) => {
const { t } = useI18N()
const { classes } = useStyles()
const [password, setPassword] = useState('')
const [error, setError] = useState(false)

useTitle(t('popups_log_out'))
useTitle(t('popups_log_out'))

const onConfirm = useCallback(() => {
if (!backupPassword || backupPassword === password) onLogout()
else setError(true)
}, [onLogout, backupPassword, password])
const onConfirm = useCallback(() => {
if (!backupPassword || backupPassword === password) onLogout()
else setError(true)
}, [onLogout, backupPassword, password])

return (
<>
<div className={classes.content}>
<Icons.CircleWarning size={64} />
<Typography className={classes.title}>{t('popups_persona_logout')}</Typography>
<div className={classes.personaContainer}>
<div className={classes.iconContainer}>
<Icons.Masks />
return (
<>
<div className={classes.content}>
<Icons.CircleWarning size={64} />
<Typography className={classes.title}>{t('popups_persona_logout')}</Typography>
<div className={classes.personaContainer}>
<div className={classes.iconContainer}>
<Icons.Masks />
</div>
<div>
<Typography className={classes.name}>{selectedPersona?.nickname}</Typography>
<Typography className={classes.identifier}>
{formatPersonaFingerprint(selectedPersona?.identifier.rawPublicKey ?? '', 10)}
</Typography>
</div>
</div>
<div>
<Typography className={classes.name}>{selectedPersona?.nickname}</Typography>
<Typography className={classes.identifier}>
{formatPersonaFingerprint(selectedPersona?.identifier.rawPublicKey ?? '', 10)}
{manageWallets.map((x, index) => {
return (
<div className={classes.personaContainer} key={index}>
<div className={classes.iconContainer}>
<Icons.SmartPay />
</div>
<div>
<Typography className={classes.name}>{x.name}</Typography>
<Typography className={classes.identifier}>
{formatEthereumAddress(x.address, 4)}
</Typography>
</div>
</div>
)
})}
<Typography className={classes.tips}>{t('popups_persona_disconnect_tip')}</Typography>
{manageWallets.length ? (
<Typography className={classes.tips} marginTop={2}>
{t('popups_persona_disconnect_manage_wallet_warning', {
persona: selectedPersona?.nickname ?? '',
addresses: manageWallets.map((x) => formatEthereumAddress(x.address, 4)).join(','),
})}
</Typography>
</div>
) : null}
</div>
<Typography className={classes.tips}>{t('popups_persona_disconnect_tip')}</Typography>
</div>

{backupPassword ? (
<div className={classes.password}>
<PasswordField
placeholder={t('popups_backup_password')}
value={password}
onChange={(e) => setPassword(e.target.value)}
error={error}
helperText={error ? t('popups_password_do_not_match') : ''}
/>
{backupPassword ? (
<div className={classes.password}>
<PasswordField
placeholder={t('popups_backup_password')}
value={password}
onChange={(e) => setPassword(e.target.value)}
error={error}
helperText={error ? t('popups_password_do_not_match') : ''}
/>
</div>
) : null}
<div className={classes.controller}>
<Button
variant="contained"
className={classes.button}
onClick={onCancel}
style={{ backgroundColor: '#F7F9FA', color: '#1C68F3' }}>
{t('cancel')}
</Button>
<LoadingButton
loading={loading}
variant="contained"
className={classes.button}
color="error"
style={{ backgroundColor: '#ff5555' }}
onClick={onConfirm}>
{t('popups_persona_logout')}
</LoadingButton>
</div>
) : null}
<div className={classes.controller}>
<Button
variant="contained"
className={classes.button}
onClick={onCancel}
style={{ backgroundColor: '#F7F9FA', color: '#1C68F3' }}>
{t('cancel')}
</Button>
<LoadingButton
loading={loading}
variant="contained"
className={classes.button}
color="error"
style={{ backgroundColor: '#ff5555' }}
onClick={onConfirm}>
{t('popups_persona_logout')}
</LoadingButton>
</div>
</>
)
})
</>
)
},
)

export default Logout

0 comments on commit 7d1bb9d

Please sign in to comment.