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

feat: Integrate safe react components #1602

Merged
merged 15 commits into from
Feb 6, 2023
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@safe-global/safe-core-sdk": "^3.3.0",
"@safe-global/safe-ethers-lib": "^1.9.0",
"@safe-global/safe-gateway-typescript-sdk": "^3.5.5",
"@safe-global/safe-react-components": "^2.0.1",
"@sentry/react": "^7.28.1",
"@sentry/tracing": "^7.28.1",
"@truffle/hdwallet-provider": "^2.1.4",
Expand Down
Binary file removed public/fonts/dm-sans-v11-latin-ext-700.woff2
Binary file not shown.
Binary file removed public/fonts/dm-sans-v11-latin-ext-regular.woff2
Binary file not shown.
6 changes: 3 additions & 3 deletions scripts/css-vars.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import palette from '../src/styles/colors.js'
import darkPalette from '../src/styles/colors-dark.js'
import reactComponents from '@safe-global/safe-react-components'
import spacings from '../src/styles/spacings.js'

const { lightPalette, darkPalette } = reactComponents
const cssVars: string[] = []
Object.entries(palette).forEach(([key, value]) => {
Object.entries(lightPalette).forEach(([key, value]) => {
Object.entries(value).forEach(([subKey, color]) => {
cssVars.push(` --color-${key}-${subKey}: ${color};`)
})
Expand Down
93 changes: 7 additions & 86 deletions src/components/common/EthHashInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,95 +1,14 @@
import { type ReactElement, useState } from 'react'
import classnames from 'classnames'
import css from './styles.module.css'
import { shortenAddress } from '@/utils/formatters'
import Identicon from '../Identicon'
import { type ReactElement } from 'react'
import { EthHashInfo } from '@safe-global/safe-react-components'
import useAddressBook from '@/hooks/useAddressBook'
import { Box, Typography } from '@mui/material'
import ExplorerLink from '@/components/common/TokenExplorerLink'
import CopyAddressButton from '@/components/common/CopyAddressButton'
import useChainId from '@/hooks/useChainId'
import { useAppSelector } from '@/store'
import { selectSettings } from '@/store/settingsSlice'
import { selectChainById } from '@/store/chainsSlice'
import useChainId from '@/hooks/useChainId'
import { ethers } from 'ethers'

type EthHashInfoProps = {
address: string
chainId?: string
name?: string | null
showAvatar?: boolean
showCopyButton?: boolean
prefix?: string
showPrefix?: boolean
copyPrefix?: boolean
shortAddress?: boolean
customAvatar?: string
hasExplorer?: boolean
avatarSize?: number
children?: React.ReactNode
}

const EthHashInfo = ({
address,
customAvatar,
prefix = '',
copyPrefix,
showPrefix,
shortAddress = true,
showAvatar = true,
avatarSize,
name,
showCopyButton,
hasExplorer,

children,
}: EthHashInfoProps): ReactElement => {
const [fallbackToIdenticon, setFallbackToIdenticon] = useState(false)
const shouldPrefix = ethers.utils.isAddress(address)

return (
<div className={css.container}>
{showAvatar && (
<div className={classnames(css.avatar, { [css.resizeAvatar]: !avatarSize })}>
{!fallbackToIdenticon && customAvatar ? (
<img
src={customAvatar}
alt={address}
onError={() => setFallbackToIdenticon(true)}
width={avatarSize}
height={avatarSize}
/>
) : (
<Identicon address={address} size={avatarSize} />
)}
</div>
)}

<div className={css.nameRow}>
{name && (
<Typography variant="body2" component="div" textOverflow="ellipsis" overflow="hidden" title={name}>
{name}
</Typography>
)}

<Box className={css.addressRow}>
<Typography fontWeight="inherit" fontSize="inherit">
{showPrefix && shouldPrefix && prefix && <b>{prefix}:</b>}
<span className={css.mobileAddress}>{shortenAddress(address)}</span>
<span className={css.desktopAddress}>{shortAddress ? shortenAddress(address) : address}</span>
</Typography>
import { getBlockExplorerLink } from '../../../utils/chains'

{showCopyButton && (
<CopyAddressButton prefix={prefix} address={address} copyPrefix={shouldPrefix && copyPrefix} />
)}

{hasExplorer && <ExplorerLink address={address} />}
{children}
</Box>
</div>
</div>
)
}
import type { EthHashInfoProps } from '@safe-global/safe-react-components'

const PrefixedEthHashInfo = ({
showName = true,
Expand All @@ -99,6 +18,7 @@ const PrefixedEthHashInfo = ({
const currentChainId = useChainId()
const chain = useAppSelector((state) => selectChainById(state, props.chainId || currentChainId))
const addressBook = useAddressBook()
const link = chain ? getBlockExplorerLink(chain, props.address) : undefined

const name = showName ? props.name || addressBook[props.address] : undefined

Expand All @@ -109,6 +29,7 @@ const PrefixedEthHashInfo = ({
copyPrefix={settings.shortName.copy}
{...props}
name={name}
ExplorerButtonProps={{ title: link?.title || '', href: link?.href || '' }}
>
{props.children}
</EthHashInfo>
Expand Down
5 changes: 2 additions & 3 deletions src/components/common/MetaTags/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { IS_PRODUCTION } from '@/config/constants'
import { ContentSecurityPolicy, StrictTransportSecurity } from '@/config/securityHeaders'
import palette from '@/styles/colors'
import darkPalette from '@/styles/colors-dark'
import { lightPalette, darkPalette } from '@safe-global/safe-react-components'

const descriptionText =
'Safe (prev. Gnosis Safe) is the most trusted platform to manage digital assets on Ethereum and multiple EVMs. Over $40B secured.'
Expand Down Expand Up @@ -34,7 +33,7 @@ const MetaTags = ({ prefetchUrl }: { prefetchUrl: string }) => (
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />

{/* PWA primary color and manifest */}
<meta name="theme-color" content={palette.background.main} media="(prefers-color-scheme: light)" />
<meta name="theme-color" content={lightPalette.background.main} media="(prefers-color-scheme: light)" />
<meta name="theme-color" content={darkPalette.background.main} media="(prefers-color-scheme: dark)" />
<link rel="manifest" href="/safe.webmanifest" />

Expand Down
22 changes: 3 additions & 19 deletions src/components/common/TokenExplorerLink/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type ReactElement } from 'react'
import { IconButton, Tooltip, SvgIcon } from '@mui/material'
import { ExplorerButton } from '@safe-global/safe-react-components'

import { useCurrentChain } from '@/hooks/useChains'
import LinkIcon from '@/public/images/common/link.svg'
import { getBlockExplorerLink } from '@/utils/chains'

const ExplorerLink = ({ address }: { address: string }): ReactElement | null => {
Expand All @@ -10,23 +10,7 @@ const ExplorerLink = ({ address }: { address: string }): ReactElement | null =>

if (!link) return null

return (
<Tooltip title={link.title} placement="top">
<IconButton href={link.href} target="_blank" rel="noopener noreferrer" size="small">
<SvgIcon
component={LinkIcon}
inheritViewBox
color="primary"
sx={{
'& path': {
fill: ({ palette }) => palette.border.main,
},
}}
fontSize="small"
/>
</IconButton>
</Tooltip>
)
return <ExplorerButton href={link.href} title={link.title} />
}

export default ExplorerLink
4 changes: 2 additions & 2 deletions src/components/new-safe/CardStepper/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useState } from 'react'
import { Box } from '@mui/system'
import { lightPalette } from '@safe-global/safe-react-components'
import css from './styles.module.css'
import { Card, LinearProgress, CardHeader, Avatar, Typography, CardContent } from '@mui/material'
import type { TxStepperProps } from './useCardStepper'
import { useCardStepper } from './useCardStepper'
import palette from '@/styles/colors'

export function CardStepper<StepperData>(props: TxStepperProps<StepperData>) {
const [progressColor, setProgressColor] = useState(palette.secondary.main)
const [progressColor, setProgressColor] = useState(lightPalette.secondary.main)
const { activeStep, onSubmit, onBack, stepData, setStep } = useCardStepper<StepperData>(props)
const { steps } = props
const currentStep = steps[activeStep]
Expand Down
4 changes: 2 additions & 2 deletions src/components/new-safe/create/steps/ReviewStep/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useMemo } from 'react'
import { Button, Grid, Typography, Divider, Box } from '@mui/material'
import { lightPalette } from '@safe-global/safe-react-components'
import ChainIndicator from '@/components/common/ChainIndicator'
import EthHashInfo from '@/components/common/EthHashInfo'
import { useCurrentChain } from '@/hooks/useChains'
Expand All @@ -20,7 +21,6 @@ import useSyncSafeCreationStep from '@/components/new-safe/create/useSyncSafeCre
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
import NetworkWarning from '@/components/new-safe/create/NetworkWarning'
import useIsWrongChain from '@/hooks/useIsWrongChain'
import palette from '@/styles/colors'
import ReviewRow from '@/components/new-safe/ReviewRow'

const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps<NewSafeFormData>) => {
Expand Down Expand Up @@ -121,7 +121,7 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps<NewSafe
<Box
p={1}
sx={{
backgroundColor: palette.secondary.background,
backgroundColor: lightPalette.secondary.background,
color: 'static.main',
width: 'fit-content',
borderRadius: '6px',
Expand Down
6 changes: 3 additions & 3 deletions src/components/new-safe/create/steps/StatusStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import useChainId from '@/hooks/useChainId'
import { getRedirect } from '@/components/new-safe/create/logic'
import layoutCss from '@/components/new-safe/create/styles.module.css'
import { AppRoutes } from '@/config/routes'
import palette from '@/styles/colors'
import { lightPalette } from '@safe-global/safe-react-components'

export const SAFE_PENDING_CREATION_STORAGE_KEY = 'pendingSafe'

Expand Down Expand Up @@ -74,9 +74,9 @@ export const CreateSafeStatus = ({ setProgressColor }: StepRenderProps<NewSafeFo
if (!setProgressColor) return

if (isError) {
setProgressColor(palette.error.main)
setProgressColor(lightPalette.error.main)
} else {
setProgressColor(palette.secondary.main)
setProgressColor(lightPalette.secondary.main)
}
}, [isError, setProgressColor])

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useState } from 'react'
import { Box, Checkbox, FormControlLabel, Typography } from '@mui/material'
import WarningAmberOutlinedIcon from '@mui/icons-material/WarningAmberOutlined'
import { lightPalette } from '@safe-global/safe-react-components'
import Domain from './Domain'
import palette from '@/styles/colors'

type UnknownAppWarningProps = {
url?: string
Expand All @@ -21,11 +21,11 @@ const UnknownAppWarning = ({ url, onHideWarning }: UnknownAppWarningProps): Reac
<Box display="flex" flexDirection="column" height="100%" alignItems="center">
<Box display="block" alignItems="center" mt={6}>
<WarningAmberOutlinedIcon fontSize="large" color="warning" />
<Typography variant="h3" fontWeight={700} mt={2} color={palette.warning.main}>
<Typography variant="h3" fontWeight={700} mt={2} color={lightPalette.warning.main}>
Warning
</Typography>
</Box>
<Typography my={2} fontWeight={700} color={palette.warning.main}>
<Typography my={2} fontWeight={700} color={lightPalette.warning.main}>
The application you are trying to access is not in the default Safe Apps list
</Typography>

Expand Down
18 changes: 5 additions & 13 deletions src/hooks/useDarkMode.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useEffect, useState } from 'react'
import { useAppSelector } from '@/store'
import { selectSettings } from '@/store/settingsSlice'
import { useEffect, useMemo, useState } from 'react'
import initTheme from '@/styles/theme'

const isSystemDarkMode = (): boolean => {
if (typeof window === 'undefined' || !window.matchMedia) return false
Expand All @@ -13,18 +12,11 @@ export const useDarkMode = (): boolean => {
const [isDarkMode, setIsDarkMode] = useState<boolean>(false)

useEffect(() => {
setIsDarkMode(settings.theme.darkMode ?? isSystemDarkMode())
const isDark = settings.theme.darkMode ?? isSystemDarkMode()

setIsDarkMode(isDark)
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light')
}, [settings.theme.darkMode])

return isDarkMode
}

export const useLightDarkTheme = () => {
const isDarkMode = useDarkMode()

useEffect(() => {
document.documentElement.setAttribute('data-theme', isDarkMode ? 'dark' : 'light')
}, [isDarkMode])

return useMemo(() => initTheme(isDarkMode), [isDarkMode])
}
21 changes: 14 additions & 7 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { type ReactElement } from 'react'
import { type AppProps } from 'next/app'
import Head from 'next/head'
import CssBaseline from '@mui/material/CssBaseline'
import type { Theme } from '@mui/material/styles'
import { ThemeProvider } from '@mui/material/styles'
import { setBaseUrl as setGatewayBaseUrl } from '@safe-global/safe-gateway-typescript-sdk'
import { CacheProvider, type EmotionCache } from '@emotion/react'
import { SafeThemeProvider } from '@safe-global/safe-react-components'
import '@/styles/globals.css'
import { IS_PRODUCTION, GATEWAY_URL_STAGING, GATEWAY_URL_PRODUCTION } from '@/config/constants'
import { StoreHydrator } from '@/store'
Expand All @@ -23,7 +25,7 @@ import { useInitSession } from '@/hooks/useInitSession'
import useStorageMigration from '@/services/ls-migration'
import Notifications from '@/components/common/Notifications'
import CookieBanner from '@/components/common/CookieBanner'
import { useLightDarkTheme } from '@/hooks/useDarkMode'
import { useDarkMode } from '@/hooks/useDarkMode'
import { cgwDebugStorage } from '@/components/sidebar/DebugToggle'
import { useTxTracking } from '@/hooks/useTxTracking'
import useGtm from '@/services/analytics/useGtm'
Expand Down Expand Up @@ -57,14 +59,19 @@ const InitApp = (): null => {
const clientSideEmotionCache = createEmotionCache()

export const AppProviders = ({ children }: { children: ReactNode | ReactNode[] }) => {
const theme = useLightDarkTheme()
const isDarkMode = useDarkMode()
const themeMode = isDarkMode ? 'dark' : 'light'

return (
<ThemeProvider theme={theme}>
<Sentry.ErrorBoundary showDialog fallback={ErrorBoundary}>
{children}
</Sentry.ErrorBoundary>
</ThemeProvider>
<SafeThemeProvider mode={themeMode}>
{(safeTheme: Theme) => (
iamacook marked this conversation as resolved.
Show resolved Hide resolved
<ThemeProvider theme={safeTheme}>
<Sentry.ErrorBoundary showDialog fallback={ErrorBoundary}>
{children}
</Sentry.ErrorBoundary>
</ThemeProvider>
)}
</SafeThemeProvider>
)
}

Expand Down
Loading