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.0-beta.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.
4 changes: 2 additions & 2 deletions scripts/css-vars.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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: palette, darkPalette } = reactComponents
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can we change this to be lightPalette as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed! thanks

const cssVars: string[] = []
Object.entries(palette).forEach(([key, value]) => {
Object.entries(value).forEach(([subKey, color]) => {
Expand Down
98 changes: 9 additions & 89 deletions src/components/common/EthHashInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,104 +1,23 @@
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 useAddressBook from '@/hooks/useAddressBook'
import { Box, Typography } from '@mui/material'
import ExplorerLink from '@/components/common/TokenExplorerLink'
import CopyAddressButton from '@/components/common/CopyAddressButton'
import { useAppSelector } from '@/store'
import { selectSettings } from '@/store/settingsSlice'
import { selectChainById } from '@/store/chainsSlice'
import useChainId from '@/hooks/useChainId'
import { ethers } from 'ethers'
import { EthHashInfo } from '@safe-global/safe-react-components'
import { getBlockExplorerLink } from '../../../utils/chains'
import { useCurrentChain } from '../../../hooks/useChains'

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>

{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,
...props
}: EthHashInfoProps & { showName?: boolean }): ReactElement => {
const settings = useAppSelector(selectSettings)
const currentChainId = useChainId()
const chain = useAppSelector((state) => selectChainById(state, props.chainId || currentChainId))
const currentChain = useCurrentChain()
const chain = useAppSelector((state) => selectChainById(state, props.chainId || currentChain?.chainId || ''))
iamacook marked this conversation as resolved.
Show resolved Hide resolved
const addressBook = useAddressBook()
const link = currentChain ? getBlockExplorerLink(currentChain, props.address) : undefined

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

Expand All @@ -109,6 +28,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
2 changes: 1 addition & 1 deletion src/components/new-safe/CardStepper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ 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'
import { lightPalette as palette } from '@safe-global/safe-react-components'

export function CardStepper<StepperData>(props: TxStepperProps<StepperData>) {
const [progressColor, setProgressColor] = useState(palette.secondary.main)
Expand Down
2 changes: 1 addition & 1 deletion src/components/new-safe/create/steps/ReviewStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ 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 { lightPalette as palette } from '@safe-global/safe-react-components'
import ReviewRow from '@/components/new-safe/ReviewRow'

const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps<NewSafeFormData>) => {
Expand Down
2 changes: 1 addition & 1 deletion 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 as palette } from '@safe-global/safe-react-components'

export const SAFE_PENDING_CREATION_STORAGE_KEY = 'pendingSafe'

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

type UnknownAppWarningProps = {
url?: string
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
64 changes: 0 additions & 64 deletions src/styles/colors-dark.ts

This file was deleted.

Loading