Skip to content

Commit

Permalink
Feat: feature flag for targeted survey (#4538)
Browse files Browse the repository at this point in the history
  • Loading branch information
katspaugh authored Nov 20, 2024
1 parent 4e824f3 commit 309db26
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 135 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { useCreateSubmissionMutation, useGetSubmissionQuery } from '@/store/api/gateway'
import { skipToken } from '@reduxjs/toolkit/query'
import { useEffect, type ReactElement } from 'react'
import { Avatar, Box, Button, Chip, IconButton, Link, Paper, Stack, ThemeProvider, Typography } from '@mui/material'
import { Close } from '@mui/icons-material'
import type { Theme } from '@mui/material/styles'
import { useAppDispatch, useAppSelector } from '@/store'
import css from './styles.module.css'
import { closeOutreachBanner, openOutreachBanner, selectOutreachBanner } from '@/store/popupSlice'
import useLocalStorage, { useSessionStorage } from '@/services/local-storage/useLocalStorage'
import useShowOutreachPopup from '@/features/targetedOutreach/hooks/useShowOutreachPopup'
import { ACTIVE_OUTREACH, OUTREACH_LS_KEY, OUTREACH_SS_KEY } from '@/features/targetedOutreach/constants'
import Track from '@/components/common/Track'
import { OUTREACH_EVENTS } from '@/services/analytics/events/outreach'
import SafeThemeProvider from '@/components/theme/SafeThemeProvider'
import useChainId from '@/hooks/useChainId'
import useSafeAddress from '@/hooks/useSafeAddress'
import useWallet from '@/hooks/wallets/useWallet'

const OutreachPopup = (): ReactElement | null => {
const dispatch = useAppDispatch()
const outreachPopup = useAppSelector(selectOutreachBanner)
const [isClosed, setIsClosed] = useLocalStorage<boolean>(OUTREACH_LS_KEY)
const currentChainId = useChainId()
const safeAddress = useSafeAddress()
const wallet = useWallet()
const [createSubmission] = useCreateSubmissionMutation()
const { data: submission } = useGetSubmissionQuery(
!wallet || !safeAddress
? skipToken
: {
outreachId: ACTIVE_OUTREACH.id,
chainId: currentChainId,
safeAddress,
signerAddress: wallet?.address,
},
)

const [askAgainLaterTimestamp, setAskAgainLaterTimestamp] = useSessionStorage<number>(OUTREACH_SS_KEY)

const shouldOpen = useShowOutreachPopup(isClosed, askAgainLaterTimestamp, submission)

const handleClose = () => {
setIsClosed(true)
dispatch(closeOutreachBanner())
}

const handleAskAgainLater = () => {
setAskAgainLaterTimestamp(Date.now())
dispatch(closeOutreachBanner())
}

// Decide whether to show the popup.
useEffect(() => {
if (shouldOpen) {
dispatch(openOutreachBanner())
} else {
dispatch(closeOutreachBanner())
}
}, [dispatch, shouldOpen])

if (!outreachPopup.open) return null

const handleOpenSurvey = async () => {
if (wallet) {
await createSubmission({
outreachId: ACTIVE_OUTREACH.id,
chainId: currentChainId,
safeAddress,
signerAddress: wallet.address,
})
}
dispatch(closeOutreachBanner())
}

return (
// Enforce light theme for the popup
<SafeThemeProvider mode="light">
{(safeTheme: Theme) => (
<ThemeProvider theme={safeTheme}>
<Box className={css.popup}>
<Paper className={css.container}>
<Stack gap={2}>
<Box display="flex">
<Avatar alt="Clem, product lead" src="/images/common/outreach-popup-avatar.png" />
<Box ml={1}>
<Typography variant="body2">Clem</Typography>
<Typography variant="body2" color="primary.light">
Product Lead
</Typography>
</Box>
</Box>
<Box>
<Chip
size="small"
sx={{ backgroundColor: 'text.primary', color: 'background.paper', mt: '-2px' }}
label={
<Typography fontWeight={700} variant="overline">
EARN REWARDS
</Typography>
}
/>
</Box>
<Typography variant="h4" fontWeight={700}>
You&apos;re invited!
</Typography>
<Typography>
As one of our top users, we&apos;d love to hear your feedback on how we can enhance Safe. Share your
contact info, and we&apos;ll reach out for a short interview.
</Typography>
<Track {...OUTREACH_EVENTS.OPEN_SURVEY}>
<Link rel="noreferrer noopener" target="_blank" href={ACTIVE_OUTREACH.url}>
<Button fullWidth variant="contained" onClick={handleOpenSurvey}>
Get Involved
</Button>
</Link>
</Track>
<Track {...OUTREACH_EVENTS.ASK_AGAIN_LATER}>
<Button fullWidth variant="text" onClick={handleAskAgainLater}>
Ask me later
</Button>
</Track>
<Typography variant="body2" color="primary.light" mx="auto">
It&apos;ll only take 2 minutes.
</Typography>
<Track {...OUTREACH_EVENTS.CLOSE_POPUP}>
<IconButton className={css.close} aria-label="close" onClick={handleClose}>
<Close />
</IconButton>
</Track>
</Stack>
</Paper>
</Box>
</ThemeProvider>
)}
</SafeThemeProvider>
)
}
export default OutreachPopup
145 changes: 10 additions & 135 deletions src/features/targetedOutreach/components/OutreachPopup/index.tsx
Original file line number Diff line number Diff line change
@@ -1,139 +1,14 @@
import { useCreateSubmissionMutation, useGetSubmissionQuery } from '@/store/api/gateway'
import { skipToken } from '@reduxjs/toolkit/query'
import { useEffect, type ReactElement } from 'react'
import { Avatar, Box, Button, Chip, IconButton, Link, Paper, Stack, ThemeProvider, Typography } from '@mui/material'
import { Close } from '@mui/icons-material'
import type { Theme } from '@mui/material/styles'
import { useAppDispatch, useAppSelector } from '@/store'
import css from './styles.module.css'
import { closeOutreachBanner, openOutreachBanner, selectOutreachBanner } from '@/store/popupSlice'
import useLocalStorage, { useSessionStorage } from '@/services/local-storage/useLocalStorage'
import useShowOutreachPopup from '@/features/targetedOutreach/hooks/useShowOutreachPopup'
import { ACTIVE_OUTREACH, OUTREACH_LS_KEY, OUTREACH_SS_KEY } from '@/features/targetedOutreach/constants'
import Track from '@/components/common/Track'
import { OUTREACH_EVENTS } from '@/services/analytics/events/outreach'
import SafeThemeProvider from '@/components/theme/SafeThemeProvider'
import useChainId from '@/hooks/useChainId'
import useSafeAddress from '@/hooks/useSafeAddress'
import useWallet from '@/hooks/wallets/useWallet'
import dynamic from 'next/dynamic'
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'

const OutreachPopup = (): ReactElement | null => {
const dispatch = useAppDispatch()
const outreachPopup = useAppSelector(selectOutreachBanner)
const [isClosed, setIsClosed] = useLocalStorage<boolean>(OUTREACH_LS_KEY)
const currentChainId = useChainId()
const safeAddress = useSafeAddress()
const wallet = useWallet()
const [createSubmission] = useCreateSubmissionMutation()
const { data: submission } = useGetSubmissionQuery(
!wallet || !safeAddress
? skipToken
: {
outreachId: ACTIVE_OUTREACH.id,
chainId: currentChainId,
safeAddress,
signerAddress: wallet?.address,
},
)
const LazyOutreachPopup = dynamic(() => import('./OutreachPopup'), {
ssr: false,
})

const [askAgainLaterTimestamp, setAskAgainLaterTimestamp] = useSessionStorage<number>(OUTREACH_SS_KEY)

const shouldOpen = useShowOutreachPopup(isClosed, askAgainLaterTimestamp, submission)

const handleClose = () => {
setIsClosed(true)
dispatch(closeOutreachBanner())
}

const handleAskAgainLater = () => {
setAskAgainLaterTimestamp(Date.now())
dispatch(closeOutreachBanner())
}

// Decide whether to show the popup.
useEffect(() => {
if (shouldOpen) {
dispatch(openOutreachBanner())
} else {
dispatch(closeOutreachBanner())
}
}, [dispatch, shouldOpen])

if (!outreachPopup.open) return null

const handleOpenSurvey = async () => {
if (wallet) {
await createSubmission({
outreachId: ACTIVE_OUTREACH.id,
chainId: currentChainId,
safeAddress,
signerAddress: wallet.address,
})
}
dispatch(closeOutreachBanner())
}

return (
// Enforce light theme for the popup
<SafeThemeProvider mode="light">
{(safeTheme: Theme) => (
<ThemeProvider theme={safeTheme}>
<Box className={css.popup}>
<Paper className={css.container}>
<Stack gap={2}>
<Box display="flex">
<Avatar alt="Clem, product lead" src="/images/common/outreach-popup-avatar.png" />
<Box ml={1}>
<Typography variant="body2">Clem</Typography>
<Typography variant="body2" color="primary.light">
Product Lead
</Typography>
</Box>
</Box>
<Box>
<Chip
size="small"
sx={{ backgroundColor: 'text.primary', color: 'background.paper', mt: '-2px' }}
label={
<Typography fontWeight={700} variant="overline">
EARN REWARDS
</Typography>
}
/>
</Box>
<Typography variant="h4" fontWeight={700}>
You&apos;re invited!
</Typography>
<Typography>
As one of our top users, we&apos;d love to hear your feedback on how we can enhance Safe. Share your
contact info, and we&apos;ll reach out for a short interview.
</Typography>
<Track {...OUTREACH_EVENTS.OPEN_SURVEY}>
<Link rel="noreferrer noopener" target="_blank" href={ACTIVE_OUTREACH.url}>
<Button fullWidth variant="contained" onClick={handleOpenSurvey}>
Get Involved
</Button>
</Link>
</Track>
<Track {...OUTREACH_EVENTS.ASK_AGAIN_LATER}>
<Button fullWidth variant="text" onClick={handleAskAgainLater}>
Ask me later
</Button>
</Track>
<Typography variant="body2" color="primary.light" mx="auto">
It&apos;ll only take 2 minutes.
</Typography>
<Track {...OUTREACH_EVENTS.CLOSE_POPUP}>
<IconButton className={css.close} aria-label="close" onClick={handleClose}>
<Close />
</IconButton>
</Track>
</Stack>
</Paper>
</Box>
</ThemeProvider>
)}
</SafeThemeProvider>
)
function OutreachPopup() {
const isEnabled = useHasFeature(FEATURES.TARGETED_SURVEY)
return isEnabled ? <LazyOutreachPopup /> : null
}

export default OutreachPopup
1 change: 1 addition & 0 deletions src/utils/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export enum FEATURES {
MULTI_CHAIN_SAFE_CREATION = 'MULTI_CHAIN_SAFE_CREATION',
MULTI_CHAIN_SAFE_ADD_NETWORK = 'MULTI_CHAIN_SAFE_ADD_NETWORK',
PROPOSERS = 'PROPOSERS',
TARGETED_SURVEY = 'TARGETED_SURVEY',
}

export const FeatureRoutes = {
Expand Down

0 comments on commit 309db26

Please sign in to comment.