Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Create Safe] feat: Status view redesign #1018

Merged
merged 10 commits into from
Nov 4, 2022
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