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: feature flag for targeted survey #4538

Merged
merged 1 commit into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading