Skip to content

Commit

Permalink
Fix: missing feature toggles (#3748)
Browse files Browse the repository at this point in the history
  • Loading branch information
katspaugh authored May 29, 2024
1 parent 01878b5 commit 1822efd
Show file tree
Hide file tree
Showing 15 changed files with 105 additions and 76 deletions.
9 changes: 7 additions & 2 deletions src/components/balances/AssetsHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import type { ReactElement, ReactNode } from 'react'
import { useMemo, type ReactElement, type ReactNode } from 'react'

import NavTabs from '@/components/common/NavTabs'
import PageHeader from '@/components/common/PageHeader'
import { balancesNavItems } from '@/components/sidebar/SidebarNavigation/config'

import css from '@/components/common/PageHeader/styles.module.css'
import { useCurrentChain } from '@/hooks/useChains'
import { isRouteEnabled } from '@/utils/chains'

const AssetsHeader = ({ children }: { children?: ReactNode }): ReactElement => {
const chain = useCurrentChain()
const navItems = useMemo(() => balancesNavItems.filter((item) => isRouteEnabled(item.href, chain)), [chain])

return (
<PageHeader
title="Assets"
action={
<div className={css.pageHeader}>
<div className={css.navWrapper}>
<NavTabs tabs={balancesNavItems} />
<NavTabs tabs={navItems} />
</div>
{children && <div className={css.actionsWrapper}>{children}</div>}
</div>
Expand Down
21 changes: 14 additions & 7 deletions src/components/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ import { CREATION_MODAL_QUERY_PARM } from '../new-safe/create/logic'
import useRecovery from '@/features/recovery/hooks/useRecovery'
import { useIsRecoverySupported } from '@/features/recovery/hooks/useIsRecoverySupported'
import ActivityRewardsSection from '@/components/dashboard/ActivityRewardsSection'
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'
const RecoveryHeader = dynamic(() => import('@/features/recovery/components/RecoveryHeader'))
const RecoveryWidget = dynamic(() => import('@/features/recovery/components/RecoveryWidget'))

const Dashboard = (): ReactElement => {
const router = useRouter()
const { safe } = useSafeInfo()
const { [CREATION_MODAL_QUERY_PARM]: showCreationModal = '' } = router.query

const showSafeApps = useHasFeature(FEATURES.SAFE_APPS)
const supportsRecovery = useIsRecoverySupported()
const [recovery] = useRecovery()
const showRecoveryWidget = supportsRecovery && !recovery
Expand Down Expand Up @@ -53,20 +55,25 @@ const Dashboard = (): ReactElement => {
</Grid>
) : null}

<Grid item xs={12} lg={showRecoveryWidget ? 12 : 6}>
<FeaturedApps stackedLayout={!showRecoveryWidget} />
</Grid>
{showSafeApps && (
<Grid item xs={12} lg={showRecoveryWidget ? 12 : 6}>
<FeaturedApps stackedLayout={!showRecoveryWidget} />
</Grid>
)}

<Grid item xs={12}>
<SafeAppsDashboardSection />
</Grid>
{showSafeApps && (
<Grid item xs={12}>
<SafeAppsDashboardSection />
</Grid>
)}

<Grid item xs={12}>
<GovernanceSection />
</Grid>
</>
)}
</Grid>

{showCreationModal ? <CreationDialog /> : null}
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { CREATE_SAFE_EVENTS, trackEvent } from '@/services/analytics'
import { waitForCreateSafeTx } from '@/services/tx/txMonitor'
import useGasPrice from '@/hooks/useGasPrice'
import { hasFeature } from '@/utils/chains'
import { FEATURES } from '@safe-global/safe-gateway-typescript-sdk'
import { FEATURES } from '@/utils/chains'
import type { DeploySafeProps } from '@safe-global/protocol-kit'
import { usePendingSafe } from './usePendingSafe'

Expand Down
33 changes: 20 additions & 13 deletions src/components/notification-center/NotificationCenter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import SettingsIcon from '@/public/images/sidebar/settings.svg'
import css from './styles.module.css'
import { trackEvent, OVERVIEW_EVENTS } from '@/services/analytics'
import SvgIcon from '@mui/icons-material/ExpandLess'
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'

const NOTIFICATION_CENTER_LIMIT = 4

Expand All @@ -33,7 +35,7 @@ const NotificationCenter = (): ReactElement => {
const [showAll, setShowAll] = useState<boolean>(false)
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null)
const open = Boolean(anchorEl)

const hasPushNotifications = useHasFeature(FEATURES.PUSH_NOTIFICATIONS)
const dispatch = useAppDispatch()

const notifications = useAppSelector(selectNotifications)
Expand Down Expand Up @@ -144,9 +146,11 @@ const NotificationCenter = (): ReactElement => {
</MuiLink>
)}
</div>

<div>
<NotificationCenterList notifications={notificationsToShow} handleClose={handleClose} />
</div>

<div className={css.popoverFooter}>
{canExpand && (
<>
Expand All @@ -166,18 +170,21 @@ const NotificationCenter = (): ReactElement => {
</Typography>
</>
)}
<Link
href={{
pathname: AppRoutes.settings.notifications,
query: router.query,
}}
passHref
legacyBehavior
>
<MuiLink className={css.settingsLink} variant="body2" onClick={onSettingsClick}>
<SvgIcon component={SettingsIcon} inheritViewBox fontSize="small" /> Push notifications settings
</MuiLink>
</Link>

{hasPushNotifications && (
<Link
href={{
pathname: AppRoutes.settings.notifications,
query: router.query,
}}
passHref
legacyBehavior
>
<MuiLink className={css.settingsLink} variant="body2" onClick={onSettingsClick}>
<SvgIcon component={SettingsIcon} inheritViewBox fontSize="small" /> Push notifications settings
</MuiLink>
</Link>
)}
</div>
</Paper>
</Popover>
Expand Down
34 changes: 24 additions & 10 deletions src/components/settings/SettingsHeader/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import SettingsHeader from '@/components/settings/SettingsHeader/index'
import { SettingsHeader } from '@/components/settings/SettingsHeader/index'
import { CONFIG_SERVICE_CHAINS } from '@/tests/mocks/chains'
import * as safeAddress from '@/hooks/useSafeAddress'
import * as feature from '@/hooks/useChains'

import { render } from '@/tests/test-utils'
import { faker } from '@faker-js/faker'
import { FEATURES } from '@/utils/chains'
import type { ChainInfo } from '@safe-global/safe-gateway-typescript-sdk'

describe('SettingsHeader', () => {
beforeEach(() => {
Expand All @@ -17,15 +19,21 @@ describe('SettingsHeader', () => {
})

it('displays safe specific preferences if on a safe', () => {
const result = render(<SettingsHeader />)
const result = render(<SettingsHeader safeAddress="0x1234" chain={CONFIG_SERVICE_CHAINS[0]} />)

expect(result.getByText('Setup')).toBeInTheDocument()
})

it('displays Notifications if feature is enabled', () => {
jest.spyOn(feature, 'useHasFeature').mockReturnValue(true)

const result = render(<SettingsHeader />)
const result = render(
<SettingsHeader
safeAddress="0x1234"
chain={{
...CONFIG_SERVICE_CHAINS[0],
features: [FEATURES.PUSH_NOTIFICATIONS] as unknown as ChainInfo['features'],
}}
/>,
)

expect(result.getByText('Notifications')).toBeInTheDocument()
})
Expand All @@ -38,7 +46,7 @@ describe('SettingsHeader', () => {
})

it('displays general preferences if no safe is open', () => {
const result = render(<SettingsHeader />)
const result = render(<SettingsHeader safeAddress="" chain={CONFIG_SERVICE_CHAINS[0]} />)

expect(result.getByText('Cookies')).toBeInTheDocument()
expect(result.getByText('Appearance')).toBeInTheDocument()
Expand All @@ -47,9 +55,15 @@ describe('SettingsHeader', () => {
})

it('displays Notifications if feature is enabled', () => {
jest.spyOn(feature, 'useHasFeature').mockReturnValue(true)

const result = render(<SettingsHeader />)
const result = render(
<SettingsHeader
safeAddress=""
chain={{
...CONFIG_SERVICE_CHAINS[0],
features: [FEATURES.PUSH_NOTIFICATIONS] as unknown as ChainInfo['features'],
}}
/>,
)

expect(result.getByText('Notifications')).toBeInTheDocument()
})
Expand Down
22 changes: 17 additions & 5 deletions src/components/settings/SettingsHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,20 @@ import PageHeader from '@/components/common/PageHeader'
import { generalSettingsNavItems, settingsNavItems } from '@/components/sidebar/SidebarNavigation/config'
import css from '@/components/common/PageHeader/styles.module.css'
import useSafeAddress from '@/hooks/useSafeAddress'
import { useCurrentChain } from '@/hooks/useChains'
import { isRouteEnabled } from '@/utils/chains'
import madProps from '@/utils/mad-props'

const SettingsHeader = (): ReactElement => {
const safeAddress = useSafeAddress()

const navItems = safeAddress ? settingsNavItems : generalSettingsNavItems
export const SettingsHeader = ({
safeAddress,
chain,
}: {
safeAddress: ReturnType<typeof useSafeAddress>
chain: ReturnType<typeof useCurrentChain>
}): ReactElement => {
const navItems = safeAddress
? settingsNavItems.filter((route) => isRouteEnabled(route.href, chain))
: generalSettingsNavItems

return (
<PageHeader
Expand All @@ -23,4 +32,7 @@ const SettingsHeader = (): ReactElement => {
)
}

export default SettingsHeader
export default madProps(SettingsHeader, {
safeAddress: useSafeAddress,
chain: useCurrentChain,
})
11 changes: 2 additions & 9 deletions src/components/sidebar/SidebarNavigation/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useMemo, type ReactElement } from 'react'
import { useRouter } from 'next/router'
import ListItem from '@mui/material/ListItem'
import { type ChainInfo, ImplementationVersionState } from '@safe-global/safe-gateway-typescript-sdk'
import { ImplementationVersionState } from '@safe-global/safe-gateway-typescript-sdk'

import {
SidebarList,
Expand All @@ -15,7 +15,7 @@ import useSafeInfo from '@/hooks/useSafeInfo'
import { AppRoutes } from '@/config/routes'
import { useQueuedTxsLength } from '@/hooks/useTxQueue'
import { useCurrentChain } from '@/hooks/useChains'
import { FeatureRoutes, hasFeature } from '@/utils/chains'
import { isRouteEnabled } from '@/utils/chains'
import { trackEvent } from '@/services/analytics'
import { SWAP_EVENTS, SWAP_LABELS } from '@/services/analytics/events/swaps'
import useIsCounterfactualSafe from '@/features/counterfactual/hooks/useIsCounterfactualSafe'
Expand All @@ -24,13 +24,6 @@ const getSubdirectory = (pathname: string): string => {
return pathname.split('/')[1]
}

const isRouteEnabled = (route: string, chain?: ChainInfo) => {
if (!chain) return false

const featureRoute = FeatureRoutes[route]
return !featureRoute || hasFeature(chain, featureRoute)
}

const Navigation = (): ReactElement => {
const chain = useCurrentChain()
const router = useRouter()
Expand Down
5 changes: 5 additions & 0 deletions src/components/tx-flow/common/TxButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import Track from '@/components/common/Track'
import { MODALS_EVENTS } from '@/services/analytics'
import { useContext } from 'react'
import { TxModalContext } from '..'
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'

const buttonSx = {
height: '58px',
Expand All @@ -27,6 +29,9 @@ export const SendTokensButton = ({ onClick, sx }: { onClick: () => void; sx?: Bu
export const SendNFTsButton = () => {
const router = useRouter()
const { setTxFlow } = useContext(TxModalContext)
const isEnabled = useHasFeature(FEATURES.ERC721)

if (!isEnabled) return null

const isNftPage = router.pathname === AppRoutes.balances.nfts
const onClick = isNftPage ? () => setTxFlow(undefined) : undefined
Expand Down
2 changes: 1 addition & 1 deletion src/components/tx-flow/flows/ExecuteBatch/ReviewBatch.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CircularProgress, Typography, Button, CardActions, Divider, Alert } from '@mui/material'
import useAsync from '@/hooks/useAsync'
import { FEATURES } from '@safe-global/safe-gateway-typescript-sdk'
import { FEATURES } from '@/utils/chains'
import type { TransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'
import { getReadOnlyMultiSendCallOnlyContract } from '@/services/contracts/safeContracts'
import { useCurrentChain } from '@/hooks/useChains'
Expand Down
2 changes: 1 addition & 1 deletion src/features/counterfactual/ActivateAccountFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { hasFeature } from '@/utils/chains'
import { hasRemainingRelays } from '@/utils/relaying'
import { Box, Button, CircularProgress, Divider, Grid, Typography } from '@mui/material'
import type { DeploySafeProps } from '@safe-global/protocol-kit'
import { FEATURES } from '@safe-global/safe-gateway-typescript-sdk'
import { FEATURES } from '@/utils/chains'
import React, { useContext, useState } from 'react'

const useActivateAccount = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/transactions/messages.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect } from 'react'
import Head from 'next/head'
import { FEATURES } from '@safe-global/safe-gateway-typescript-sdk'
import { FEATURES } from '@/utils/chains'
import { useRouter } from 'next/router'
import type { NextPage } from 'next'

Expand Down
Loading

0 comments on commit 1822efd

Please sign in to comment.