Skip to content

Commit

Permalink
[Create Safe] feat: Status view redesign (#1018)
Browse files Browse the repository at this point in the history
* feat: Implement create safe status redesign

* style: Add animated loading spinner

* fix: Simplify conditions

* add: animate into css safe logo

* fix: use numeric enum for simpler conditions, adjust animated logo sizes

* fix: Display dialog after safe creation

* fix: Feedback

* fix: Better name for query param

Co-authored-by: schmanu <manu@safe.global>
  • Loading branch information
usame-algan and schmanu authored Nov 4, 2022
1 parent 6b05e27 commit 872f5f9
Show file tree
Hide file tree
Showing 24 changed files with 1,246 additions and 117 deletions.
2 changes: 1 addition & 1 deletion src/components/create-safe/status/useSafeCreation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import type { EthersError } from '@/utils/ethers-utils'

export enum SafeCreationStatus {
AWAITING = 'AWAITING',
WALLET_REJECTED = 'WALLET_REJECTED',
PROCESSING = 'PROCESSING',
WALLET_REJECTED = 'WALLET_REJECTED',
ERROR = 'ERROR',
REVERTED = 'REVERTED',
TIMEOUT = 'TIMEOUT',
Expand Down
2 changes: 2 additions & 0 deletions src/components/create-safe/status/useSafeCreationEffects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ const getRedirect = (chainId: string, safeAddress: string, redirectQuery?: strin
// Otherwise, redirect to the provided URL (e.g. from a Safe App)

// Track the redirect to Safe App
// TODO: Narrow this down to /apps only
if (redirectUrl.includes('apps')) {
trackEvent(SAFE_APPS_EVENTS.SHARED_APP_OPEN_AFTER_SAFE_CREATION)
}

// We're prepending the safe address directly here because the `router.push` doesn't parse
// The URL for already existing query params
// TODO: Check if we can accomplish this with URLSearchParams or URL instead
const hasQueryParams = redirectUrl.includes('?')
const appendChar = hasQueryParams ? '&' : '?'
return redirectUrl + `${appendChar}safe=${address}`
Expand Down
76 changes: 76 additions & 0 deletions src/components/dashboard/CreationDialog/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { type ElementType } from 'react'
import { Box, Button, Dialog, DialogContent, Grid, SvgIcon, Typography } from '@mui/material'

import HomeIcon from '@/public/images/sidebar/home.svg'
import TransactionIcon from '@/public/images/sidebar/transactions.svg'
import AppsIcon from '@/public/images/sidebar/apps.svg'
import SettingsIcon from '@/public/images/sidebar/settings.svg'
import BeamerIcon from '@/public/images/sidebar/whats-new.svg'
import HelpCenterIcon from '@/public/images/sidebar/help-center.svg'

const HintItem = ({ Icon, title, description }: { Icon: ElementType; title: string; description: string }) => {
return (
<Grid item md={6}>
<Box display="flex" alignItems="center" gap={1} mb={1}>
<SvgIcon component={Icon} inheritViewBox fontSize="small" />
<Typography variant="subtitle2" fontWeight="700">
{title}
</Typography>
</Box>

<Typography variant="body2">{description}</Typography>
</Grid>
)
}

const CreationDialog = () => {
const [open, setOpen] = React.useState(true)

return (
<Dialog open={open}>
<DialogContent sx={{ paddingX: 8, paddingTop: 9, paddingBottom: 6 }}>
<Typography variant="h3" fontWeight="700" mb={1}>
Welcome to your Safe!
</Typography>
<Typography variant="body2">
Congratulations on creating the safest wallet in web3. Keep your assets safe and discover our app.
</Typography>
<Grid container mt={4} mb={6} spacing={3}>
<HintItem
Icon={HomeIcon}
title="Home"
description="Get a status overview of your Safe and more on your Safe homepage."
/>
<HintItem
Icon={TransactionIcon}
title="Transactions"
description="Review, approve, execute and keep track of asset movement."
/>
<HintItem
Icon={AppsIcon}
title="Apps"
description="Over 70 dApps available for you on Mainnet. Check out Safe apps for secure integrations. "
/>
<HintItem
Icon={SettingsIcon}
title="Settings"
description="Want to change your Safe setup? Settings is the right place to go."
/>
<HintItem Icon={BeamerIcon} title="What's new" description="Don't miss any future Safe updates." />
<HintItem
Icon={HelpCenterIcon}
title="Help center"
description="Have any questions? Check out our collection of articles. "
/>
</Grid>
<Box display="flex" justifyContent="center">
<Button onClick={() => setOpen(false)} variant="contained" size="stretched">
Got it
</Button>
</Box>
</DialogContent>
</Dialog>
)
}

export default CreationDialog
34 changes: 21 additions & 13 deletions src/components/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,34 @@ import PendingTxsList from '@/components/dashboard/PendingTxs/PendingTxsList'
import Overview from '@/components/dashboard/Overview/Overview'
import { FeaturedApps } from '@/components/dashboard/FeaturedApps/FeaturedApps'
import SafeAppsDashboardSection from '@/components/dashboard/SafeAppsDashboardSection/SafeAppsDashboardSection'
import CreationDialog from '@/components/dashboard/CreationDialog'
import { useRouter } from 'next/router'

const Dashboard = (): ReactElement => {
const router = useRouter()
const { showCreationModal = '' } = router.query

return (
<Grid container spacing={3}>
<Grid item xs={12} md={12} lg={6}>
<Overview />
</Grid>
<>
<Grid container spacing={3}>
<Grid item xs={12} md={12} lg={6}>
<Overview />
</Grid>

<Grid item xs={12} md={12} lg={6}>
<PendingTxsList size={4} />
</Grid>
<Grid item xs={12} md={12} lg={6}>
<PendingTxsList size={4} />
</Grid>

<Grid item xs={12}>
<FeaturedApps />
</Grid>
<Grid item xs={12}>
<FeaturedApps />
</Grid>

<Grid item xs={12}>
<SafeAppsDashboardSection />
<Grid item xs={12}>
<SafeAppsDashboardSection />
</Grid>
</Grid>
</Grid>
{showCreationModal ? <CreationDialog /> : null}
</>
)
}

Expand Down
26 changes: 14 additions & 12 deletions src/components/new-safe/CardStepper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ export function CardStepper<StepperData>(props: TxStepperProps<StepperData>) {
return (
<Card className={css.card}>
<LinearProgress color="secondary" variant="determinate" value={Math.min(progress, 100)} />
<CardHeader
title={currentStep.title}
subheader={currentStep.subtitle}
titleTypographyProps={{ variant: 'h4' }}
subheaderTypographyProps={{ variant: 'body2' }}
avatar={
<Avatar className={css.step}>
<Typography variant="body2">{activeStep + 1}</Typography>
</Avatar>
}
className={css.header}
/>
{currentStep.title && (
<CardHeader
title={currentStep.title}
subheader={currentStep.subtitle}
titleTypographyProps={{ variant: 'h4' }}
subheaderTypographyProps={{ variant: 'body2' }}
avatar={
<Avatar className={css.step}>
<Typography variant="body2">{activeStep + 1}</Typography>
</Avatar>
}
className={css.header}
/>
)}
<CardContent className={css.content}>{currentStep.render(stepData, onSubmit, onBack, setStep)}</CardContent>
</Card>
)
Expand Down
2 changes: 1 addition & 1 deletion src/components/new-safe/CardStepper/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

.header {
padding: var(--space-3) var(--space-2);
border-bottom: 1px solid var(--color-border-light);
}

.header :global .MuiCardHeader-title {
Expand All @@ -22,7 +23,6 @@

.content {
padding: var(--space-3) 52px;
border-top: 1px solid var(--color-border-light);
border-bottom: 1px solid var(--color-border-light);
}

Expand Down
12 changes: 11 additions & 1 deletion src/components/new-safe/CreateSafe/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import CreateSafeStep0 from '@/components/new-safe/steps/Step0'
import CreateSafeStep1 from '@/components/new-safe/steps/Step1'
import CreateSafeStep2 from '@/components/new-safe/steps/Step2'
import CreateSafeStep3 from '@/components/new-safe/steps/Step3'
import { CreateSafeStatus } from '@/components/new-safe/steps/Step4'
import useAddressBook from '@/hooks/useAddressBook'
import { CardStepper } from '../CardStepper'

import { AppRoutes } from '@/config/routes'
import { CREATE_SAFE_CATEGORY } from '@/services/analytics'
import type { AlertColor } from '@mui/material'
Expand All @@ -24,6 +24,8 @@ export type NewSafeFormData = {
threshold: number
owners: NamedAddress[]
mobileOwners: NamedAddress[]
saltNonce: number
safeAddress?: string
}

const staticHints: Record<
Expand Down Expand Up @@ -143,6 +145,13 @@ const CreateSafe = () => {
<CreateSafeStep3 data={data} onSubmit={onSubmit} onBack={onBack} setStep={setStep} />
),
},
{
title: '',
subtitle: '',
render: (data, onSubmit, onBack, setStep) => (
<CreateSafeStatus data={data} onSubmit={onSubmit} onBack={onBack} setStep={setStep} />
),
},
]

const staticHint = useMemo(() => staticHints[activeStep], [activeStep])
Expand All @@ -152,6 +161,7 @@ const CreateSafe = () => {
mobileOwners: [] as NamedAddress[],
owners: [defaultOwner],
threshold: 1,
saltNonce: Date.now(),
}

const onClose = () => {
Expand Down
23 changes: 23 additions & 0 deletions src/components/new-safe/CreateSafe/useSetCreationStep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useEffect } from 'react'
import useLocalStorage from '@/services/local-storage/useLocalStorage'
import type { StepRenderProps } from '@/components/new-safe/CardStepper/useCardStepper'
import type { PendingSafeData } from '@/components/new-safe/steps/Step4'
import type { NewSafeFormData } from '@/components/new-safe/CreateSafe/index'
import { SAFE_PENDING_CREATION_STORAGE_KEY } from '@/components/new-safe/steps/Step4'

const useSetCreationStep = (setStep: StepRenderProps<NewSafeFormData>['setStep'], isConnected: boolean) => {
const [pendingSafe] = useLocalStorage<PendingSafeData | undefined>(SAFE_PENDING_CREATION_STORAGE_KEY)

useEffect(() => {
if (!isConnected) {
setStep(0)
}

// Jump to the status screen if there is already a tx submitted
if (pendingSafe) {
setStep(4)
}
}, [isConnected, setStep, pendingSafe])
}

export default useSetCreationStep
8 changes: 5 additions & 3 deletions src/components/new-safe/steps/Step0/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import type { NewSafeFormData } from '@/components/new-safe/CreateSafe'
import type { StepRenderProps } from '@/components/new-safe/CardStepper/useCardStepper'
import WalletDetails from '@/components/common/ConnectWallet/WalletDetails'
import PairingDetails from '@/components/common/PairingDetails'
import useCreateSafe from '@/components/new-safe/CreateSafe/useCreateSafe'
import type { ConnectedWallet } from '@/services/onboard'
import useIsConnected from '@/hooks/useIsConnected'
import useSetCreationStep from '@/components/new-safe/CreateSafe/useSetCreationStep'

export const ConnectWalletContent = ({ onSubmit }: { onSubmit: StepRenderProps<NewSafeFormData>['onSubmit'] }) => {
const isWrongChain = useIsWrongChain()
Expand Down Expand Up @@ -56,8 +57,9 @@ export const ConnectWalletContent = ({ onSubmit }: { onSubmit: StepRenderProps<N
)
}

const CreateSafeStep0 = ({ onSubmit, onBack }: StepRenderProps<NewSafeFormData>) => {
const { isConnected } = useCreateSafe()
const CreateSafeStep0 = ({ onSubmit, onBack, setStep }: StepRenderProps<NewSafeFormData>) => {
const isConnected = useIsConnected()
useSetCreationStep(setStep, isConnected)

return (
<Paper>
Expand Down
7 changes: 5 additions & 2 deletions src/components/new-safe/steps/Step1/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import InfoIcon from '@/public/images/notifications/info.svg'
import NetworkSelector from '@/components/common/NetworkSelector'
import type { StepRenderProps } from '../../CardStepper/useCardStepper'
import type { NewSafeFormData } from '../../CreateSafe'
import useCreateSafe from '@/components/new-safe/CreateSafe/useCreateSafe'
import useIsConnected from '@/hooks/useIsConnected'
import useSetCreationStep from '@/components/new-safe/CreateSafe/useSetCreationStep'

import css from './styles.module.css'

Expand All @@ -34,10 +35,12 @@ function CreateSafeStep1({
data,
onSubmit,
onBack,
setStep,
setSafeName,
}: StepRenderProps<NewSafeFormData> & { setSafeName: (name: string) => void }) {
const fallbackName = useMnemonicSafeName()
const { isConnected } = useCreateSafe()
const isConnected = useIsConnected()
useSetCreationStep(setStep, isConnected)

const {
handleSubmit,
Expand Down
7 changes: 5 additions & 2 deletions src/components/new-safe/steps/Step2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import type { StepRenderProps } from '../../CardStepper/useCardStepper'
import type { NewSafeFormData } from '../../CreateSafe'
import type { CreateSafeInfoItem } from '../../CreateSafeInfos'
import { useSafeSetupHints } from './useSafeSetupHints'
import useCreateSafe from '@/components/new-safe/CreateSafe/useCreateSafe'
import useIsConnected from '@/hooks/useIsConnected'
import useSetCreationStep from '@/components/new-safe/CreateSafe/useSetCreationStep'

export type CreateSafeStep2Form = {
owners: NamedAddress[]
Expand All @@ -28,11 +29,13 @@ const CreateSafeStep2 = ({
onSubmit,
onBack,
data,
setStep,
setDynamicHint,
}: StepRenderProps<NewSafeFormData> & {
setDynamicHint: (hints: CreateSafeInfoItem | undefined) => void
}): ReactElement => {
const { isConnected } = useCreateSafe()
const isConnected = useIsConnected()
useSetCreationStep(setStep, isConnected)

const formMethods = useForm<CreateSafeStep2Form>({
mode: 'all',
Expand Down
Loading

0 comments on commit 872f5f9

Please sign in to comment.