Onboarding Update

This commit is contained in:
sahil-tgs 2024-07-16 00:12:28 +05:30
parent 6a9e6263ad
commit d61d4f4e11

View File

@ -11,28 +11,117 @@ import {
LinearProgress, LinearProgress,
Link, Link,
Typography, Typography,
Accordion, Stepper,
AccordionSummary, Step,
AccordionDetails, StepLabel,
StepConnector,
styled,
stepConnectorClasses,
Paper,
useTheme,
useMediaQuery,
IconButton,
} from '@mui/material'; } from '@mui/material';
import { type Robot } from '../../models'; import {
import { Casino, Bolt, Check, Storefront, AddBox, School } from '@mui/icons-material'; Check,
Casino,
SmartToy,
Storefront,
AddBox,
School,
ContentCopy,
} from '@mui/icons-material';
import RobotAvatar from '../../components/RobotAvatar'; import RobotAvatar from '../../components/RobotAvatar';
import TokenInput from './TokenInput'; import TokenInput from './TokenInput';
import { genBase62Token } from '../../utils'; import { genBase62Token } from '../../utils';
import { NewTabIcon } from '../../components/Icons';
import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; import { AppContext, type UseAppStoreType } from '../../contexts/AppContext';
import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext';
interface OnboardingProps { const StyledPaper = styled(Paper)(({ theme }) => ({
setView: (state: 'welcome' | 'onboarding' | 'recovery' | 'profile') => void; backgroundColor: theme.palette.background.paper,
robot: Robot; boxShadow: '8px 8px 0px 0px rgba(0, 0, 0, 0.2)',
setRobot: (state: Robot) => void; borderRadius: '16px',
inputToken: string; border: '2px solid #000',
setInputToken: (state: string) => void; padding: theme.spacing(2),
getGenerateRobot: (token: string) => void; color: theme.palette.text.primary,
badToken: string; width: '100%',
baseUrl: string; maxWidth: '500px',
margin: '0 auto',
}));
const StyledButton = styled(Button)(({ theme }) => ({
justifyContent: 'center',
textAlign: 'center',
padding: theme.spacing(1),
borderRadius: '8px',
border: '2px solid #000',
boxShadow: '4px 4px 0px 0px rgba(0, 0, 0, 0.2)',
textTransform: 'none',
fontWeight: 'bold',
width: '100%',
'&:hover': {
boxShadow: '6px 6px 0px 0px rgba(0, 0, 0, 0.3)',
},
}));
const StyledStepConnector = styled(StepConnector)(({ theme }) => ({
[`&.${stepConnectorClasses.alternativeLabel}`]: {
top: 22,
left: 'calc(-50% + 20px)',
right: 'calc(50% + 20px)',
},
[`&.${stepConnectorClasses.active}`]: {
[`& .${stepConnectorClasses.line}`]: {
borderColor: theme.palette.primary.main,
},
},
[`&.${stepConnectorClasses.completed}`]: {
[`& .${stepConnectorClasses.line}`]: {
borderColor: theme.palette.primary.main,
},
},
[`& .${stepConnectorClasses.line}`]: {
borderColor: theme.palette.mode === 'dark' ? theme.palette.grey[800] : '#eaeaf0',
borderTopWidth: 3,
borderRadius: 1,
},
}));
const StyledStepIconRoot = styled('div')<{ ownerState: { active?: boolean; completed?: boolean } }>(
({ theme, ownerState }) => ({
backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[700] : '#ccc',
zIndex: 1,
color: '#fff',
width: 44,
height: 44,
display: 'flex',
borderRadius: '50%',
justifyContent: 'center',
alignItems: 'center',
...(ownerState.active && {
backgroundColor: theme.palette.primary.main,
boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)',
}),
...(ownerState.completed && {
backgroundColor: theme.palette.primary.main,
}),
}),
);
function StyledStepIcon(props: StepIconProps) {
const { active, completed, className } = props;
const icons: { [index: string]: React.ReactElement } = {
1: <Casino />,
2: <SmartToy />,
3: <Storefront />,
};
return (
<StyledStepIconRoot ownerState={{ active, completed }} className={className}>
{icons[String(props.icon)]}
</StyledStepIconRoot>
);
} }
const Onboarding = ({ const Onboarding = ({
@ -44,6 +133,8 @@ const Onboarding = ({
}: OnboardingProps): JSX.Element => { }: OnboardingProps): JSX.Element => {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const { setPage } = useContext<UseAppStoreType>(AppContext); const { setPage } = useContext<UseAppStoreType>(AppContext);
const { garage } = useContext<UseGarageStoreType>(GarageContext); const { garage } = useContext<UseGarageStoreType>(GarageContext);
@ -63,43 +154,77 @@ const Onboarding = ({
const slot = garage.getSlot(); const slot = garage.getSlot();
const steps = [
t('1. Generate a token'),
t('2. Meet your robot identity'),
t('3. Browse or create an order'),
];
return ( return (
<Box> <Box
<Accordion expanded={step === '1'} disableGutters={true}> sx={{
<AccordionSummary> mt: 3,
<Typography variant='h5' color={step === '1' ? 'text.primary' : 'text.disabled'}> mb: 3,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
px: 2,
width: '100%',
}}
>
<Stepper
alternativeLabel={!isMobile}
orientation={isMobile ? 'horizontal' : 'horizontal'}
activeStep={parseInt(step) - 1}
connector={<StyledStepConnector />}
sx={{ width: '100%', mb: 3 }}
>
{steps.map((label) => (
<Step key={label}>
<StepLabel StepIconComponent={StyledStepIcon}>{isMobile ? null : label}</StepLabel>
</Step>
))}
</Stepper>
<StyledPaper elevation={3}>
{step === '1' && (
<>
<Typography variant='h6' gutterBottom align='center'>
{t('1. Generate a token')} {t('1. Generate a token')}
</Typography> </Typography>
</AccordionSummary> <Typography variant='body2' align='center' sx={{ mb: 2 }}>
<AccordionDetails>
<Grid container direction='column' alignItems='center' spacing={1} padding={1}>
<Grid item>
<Typography>
{t( {t(
'This temporary key gives you access to a unique and private robot identity for your trade.', 'This temporary key gives you access to a unique and private robot identity for your trade.',
)} )}
</Typography> </Typography>
</Grid>
{!generatedToken ? ( {!generatedToken ? (
<Grid item> <Box display='flex' justifyContent='center'>
<Button autoFocus onClick={generateToken} variant='contained' size='large'> <StyledButton
<Casino /> onClick={generateToken}
{t('Generate token')} variant='contained'
</Button> size='large'
</Grid> fullWidth={false}
>
{t('Generate Token')}
</StyledButton>
</Box>
) : ( ) : (
<Grid item>
<Collapse in={generatedToken}> <Collapse in={generatedToken}>
<Grid container direction='column' alignItems='center' spacing={1}> <Grid container direction='column' alignItems='center' spacing={2}>
<Grid item> <Grid item xs={12}>
<Alert variant='outlined' severity='info'> <Alert variant='outlined' severity='info' sx={{ mb: 2 }}>
<b>{`${t('Store it somewhere safe!')} `}</b> <Typography variant='body2'>
<strong>{t('Store it somewhere safe!')}</strong>
</Typography>
<Typography variant='body2'>
{t( {t(
`This token is the one and only key to your robot and trade. You will need it later to recover your order or check its status.`, 'This token is the one and only key to your robot and trade. You will need it later to recover your order or check its status.',
)} )}
</Typography>
</Alert> </Alert>
</Grid> </Grid>
<Grid item sx={{ width: '100%' }}> <Grid item xs={12} sx={{ width: '100%' }}>
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<TokenInput <TokenInput
loading={loading} loading={loading}
autoFocusTarget='copyButton' autoFocusTarget='copyButton'
@ -107,203 +232,156 @@ const Onboarding = ({
setInputToken={setInputToken} setInputToken={setInputToken}
badToken={badToken} badToken={badToken}
onPressEnter={() => null} onPressEnter={() => null}
sx={{ flexGrow: 1 }}
/> />
<IconButton
onClick={() => navigator.clipboard.writeText(inputToken)}
size='small'
>
<ContentCopy />
</IconButton>
</Box>
</Grid> </Grid>
<Grid item> <Grid item xs={12}>
<Typography> <Box display='flex' justifyContent='center'>
{t('You can also add your own random characters into the token or')} <StyledButton
<Button size='small' onClick={generateToken}>
<Casino />
{t('roll again')}
</Button>
</Typography>
</Grid>
<Grid item>
<Button
onClick={() => { onClick={() => {
setStep('2'); setStep('2');
getGenerateRobot(inputToken); getGenerateRobot(inputToken);
}} }}
variant='contained' variant='contained'
size='large' size='large'
startIcon={<Check />}
fullWidth={false}
> >
<Check />
{t('Continue')} {t('Continue')}
</Button> </StyledButton>
</Box>
</Grid> </Grid>
</Grid> </Grid>
</Collapse> </Collapse>
</Grid>
)} )}
</Grid>
</AccordionDetails>
</Accordion>
<Accordion expanded={step === '2'} disableGutters={true}>
<AccordionSummary>
<Typography variant='h5' color={step === '2' ? 'text.primary' : 'text.disabled'}>
{t('2. Meet your robot identity')}
</Typography>
</AccordionSummary>
<AccordionDetails>
<Grid container direction='column' alignItems='center' spacing={1}>
<Grid item>
<Typography>
{slot?.hashId ? (
t('This is your trading avatar')
) : (
<>
<b>{t('Building your robot!')}</b>
<LinearProgress />
</> </>
)} )}
</Typography>
</Grid>
<Grid item sx={{ width: '13.5em' }}> {step === '2' && (
<>
<Typography variant='h6' gutterBottom align='center'>
{t('2. Meet your robot identity')}
</Typography>
<Typography variant='body2' align='center' sx={{ mb: 2 }}>
{slot?.hashId ? t('This is your trading avatar') : t('Building your robot!')}
</Typography>
{!slot?.hashId && <LinearProgress sx={{ mb: 2 }} />}
<Box display='flex' justifyContent='center' sx={{ mb: 2 }}>
<RobotAvatar <RobotAvatar
hashId={slot?.hashId ?? ''} hashId={slot?.hashId ?? ''}
smooth={true} smooth={true}
style={{ maxWidth: '12.5em', maxHeight: '12.5em' }} style={{ width: '150px', height: '150px' }}
placeholderType='generating' placeholderType='generating'
imageStyle={{ imageStyle={{
transform: '',
border: '2px solid #555', border: '2px solid #555',
filter: 'drop-shadow(1px 1px 1px #000000)', borderRadius: '50%',
height: '12.4em', boxShadow: '0 4px 10px rgba(0,0,0,0.3)',
width: '12.4em',
}} }}
tooltipPosition='top' tooltipPosition='top'
/> />
</Grid> </Box>
{slot?.nickname && (
{slot?.nickname ? ( <>
<Grid item> <Typography variant='body2' align='center'>
<Typography align='center'>{t('Hi! My name is')}</Typography> {t('Hi! My name is')}
<Typography component='h5' variant='h5'>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexWrap: 'wrap',
}}
>
<Bolt
sx={{
color: '#fcba03',
height: '1.5em',
width: '1.5em',
}}
/>
<b>{slot?.nickname}</b>
<Bolt
sx={{
color: '#fcba03',
height: '1.5em',
width: '1.5em',
}}
/>
</div>
</Typography> </Typography>
</Grid> <Typography variant='h6' align='center' sx={{ mt: 1, mb: 2 }}>
) : null} <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Grid item> <SmartToy sx={{ color: '#fcba03', fontSize: '1.2em', mr: 1 }} />
<Collapse in={!!slot?.hashId}> <strong>{slot.nickname}</strong>
<Button <SmartToy sx={{ color: '#fcba03', fontSize: '1.2em', ml: 1 }} />
onClick={() => { </Box>
setStep('3'); </Typography>
}} </>
)}
<Box display='flex' justifyContent='center'>
<StyledButton
onClick={() => setStep('3')}
variant='contained' variant='contained'
size='large' size='large'
startIcon={<Check />}
disabled={!slot?.hashId}
fullWidth={false}
> >
<Check />
{t('Continue')} {t('Continue')}
</Button> </StyledButton>
</Collapse> </Box>
</Grid> </>
</Grid> )}
</AccordionDetails>
</Accordion>
<Accordion expanded={step === '3'} disableGutters={true}> {step === '3' && (
<AccordionSummary> <>
<Typography variant='h5' color={step === '3' ? 'text.primary' : 'text.disabled'}> <Typography variant='h6' gutterBottom align='center'>
{t('3. Browse or create an order')} {t('3. Browse or create an order')}
</Typography> </Typography>
</AccordionSummary> <Typography variant='body2' align='center' sx={{ mb: 2 }}>
<AccordionDetails>
<Grid container direction='column' alignItems='center' spacing={1} padding={1.5}>
<Grid item>
<Typography>
{t( {t(
'RoboSats is a peer-to-peer marketplace. You can browse the public offers or create a new one.', 'RoboSats is a peer-to-peer marketplace. You can browse the public offers or create a new one.',
)} )}
</Typography> </Typography>
</Grid> <Box display='flex' justifyContent='center' sx={{ mb: 2 }}>
<ButtonGroup
<Grid item> variant='contained'
<ButtonGroup variant='contained'> size='large'
<Button orientation={isMobile ? 'vertical' : 'horizontal'}
color='primary' fullWidth={isMobile}
>
<StyledButton
onClick={() => { onClick={() => {
navigate('/offers'); navigate('/offers');
setPage('offers'); setPage('offers');
}} }}
startIcon={<Storefront />}
> >
<Storefront /> <div style={{ width: '0.5em' }} />
{t('Offers')} {t('Offers')}
</Button> </StyledButton>
<Button <StyledButton
color='secondary'
onClick={() => { onClick={() => {
navigate('/create'); navigate('/create');
setPage('create'); setPage('create');
}} }}
startIcon={<AddBox />}
color='secondary'
> >
<AddBox /> <div style={{ width: '0.5em' }} />
{t('Create')} {t('Create')}
</Button> </StyledButton>
</ButtonGroup> </ButtonGroup>
</Grid> </Box>
<Typography variant='body2' align='center' sx={{ mb: 2 }}>
<Grid item> {t('If you need help on your RoboSats journey join our public support')}{' '}
<Typography> <Link href='https://t.me/robosats_es' target='_blank' rel='noreferrer'>
{`${t('If you need help on your RoboSats journey join our public support')} `}
<Link target='_blank' href='https://t.me/robosats_es' rel='noreferrer'>
{t('Telegram group')} {t('Telegram group')}
</Link> </Link>
{`, ${t('or visit the robot school for documentation.')} `} , {t('or visit the robot school for documentation.')}
</Typography> </Typography>
</Grid> <Box display='flex' justifyContent='center' sx={{ mb: 2 }}>
<Grid item> <StyledButton
<Button
component={Link} component={Link}
href='https://learn.robosats.com' href='https://learn.robosats.com'
target='_blank' target='_blank'
color='inherit' color='inherit'
variant='contained' variant='contained'
startIcon={<School />}
fullWidth={isMobile}
> >
<School /> <div style={{ width: '0.5em' }} />
{t('Learn RoboSats')} {t('Learn RoboSats')}
<div style={{ width: '0.5em' }} /> </StyledButton>
<NewTabIcon sx={{ width: '0.8em' }} /> </Box>
<Box display='flex' justifyContent='center'>
<Button color='inherit' onClick={() => setView('profile')}>
{t('See Profile')}
</Button> </Button>
</Grid> </Box>
<Grid item sx={{ position: 'relative', top: '0.6em' }}> </>
<Button )}
color='inherit' </StyledPaper>
onClick={() => {
setView('profile');
}}
>
{t('See profile')}
</Button>
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
</Box> </Box>
); );
}; };