feat: add survey popup for survey monkey (#3116)

* add survey popup for survey monkey

* update useEffect and add 24 hour window

* upate % logic

* update logic to show after duration

* update timestamp conditional

* small changes
This commit is contained in:
Ian Lapham 2022-01-14 11:33:31 -07:00 committed by GitHub
parent 5d97cbf6ad
commit 1b10c88c51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 145 additions and 2 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 572 KiB

@ -0,0 +1,106 @@
import { Trans } from '@lingui/macro'
import { AutoColumn } from 'components/Column'
import { RowFixed } from 'components/Row'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import { useEffect } from 'react'
import { MessageCircle, X } from 'react-feather'
import ReactGA from 'react-ga'
import { useShowSurveyPopup } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { ExternalLink, ThemedText, Z_INDEX } from 'theme'
import BGImage from '../../assets/images/survey-orb.svg'
import useTheme from '../../hooks/useTheme'
const Wrapper = styled(AutoColumn)`
background: #edeef2;
position: relative;
border-radius: 12px;
padding: 18px;
max-width: 360px;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
color: ${({ theme }) => theme.text1};
overflow: hidden;
${({ theme }) => theme.mediaWidth.upToSmall`
max-width: 100%;
`}
`
const BGOrb = styled.img`
position: absolute;
right: -64px;
top: -64px;
width: 180px;
z-index: ${Z_INDEX.sticky};
`
const WrappedCloseIcon = styled(X)`
position: absolute;
top: 10px;
right: 10px;
width: 20px;
height: 20px;
stroke: #7c7c80;
z-index: ${Z_INDEX.fixed};
:hover {
cursor: pointer;
opacity: 0.8;
}
`
const END_TIMESTAMP = 1642215971 // Jan 15th
export default function SurveyPopup() {
const theme = useTheme()
const [showPopup, setShowSurveyPopup] = useShowSurveyPopup()
// show popup to 1% of users
useEffect(() => {
// has not visited page during A/B testing if undefined
if (showPopup === undefined) {
if (Math.random() < 0.01) {
setShowSurveyPopup(true)
// log a case of succesful view
ReactGA.event({
category: 'Survey',
action: 'Saw Survey',
})
}
}
}, [setShowSurveyPopup, showPopup])
// limit survey to 24 hours based on timestamps
const timestamp = useCurrentBlockTimestamp()
const durationOver = timestamp ? timestamp.toNumber() > END_TIMESTAMP : false
return (
<>
{!showPopup || durationOver ? null : (
<Wrapper gap="10px">
<WrappedCloseIcon
onClick={() => {
ReactGA.event({
category: 'Survey',
action: 'Clicked Survey Link',
})
setShowSurveyPopup(false)
}}
/>
<BGOrb src={BGImage} />
<ExternalLink href="https://www.surveymonkey.com/r/YGWV9VD">
<RowFixed>
<MessageCircle stroke={theme.black} size="20px" strokeWidth="1px" />
<ThemedText.White fontWeight={600} color={theme.black} ml="6px">
<Trans>Tell us what you think </Trans>
</ThemedText.White>
</RowFixed>
</ExternalLink>
<ThemedText.Black style={{ zIndex: Z_INDEX.fixed }} fontWeight={400} fontSize="12px" color={theme.black}>
<Trans>Take a 10 minute survey to help us improve your experience in the Uniswap app.</Trans>
</ThemedText.Black>
</Wrapper>
)}
</>
)
}

@ -4,10 +4,11 @@ import styled from 'styled-components/macro'
import { MEDIA_WIDTHS } from 'theme' import { MEDIA_WIDTHS } from 'theme'
import { useActivePopups } from '../../state/application/hooks' import { useActivePopups } from '../../state/application/hooks'
import { useURLWarningVisible } from '../../state/user/hooks' import { useShowSurveyPopup, useURLWarningVisible } from '../../state/user/hooks'
import { AutoColumn } from '../Column' import { AutoColumn } from '../Column'
import ClaimPopup from './ClaimPopup' import ClaimPopup from './ClaimPopup'
import PopupItem from './PopupItem' import PopupItem from './PopupItem'
import SurveyPopup from './SurveyPopup'
const MobilePopupWrapper = styled.div<{ height: string | number }>` const MobilePopupWrapper = styled.div<{ height: string | number }>`
position: relative; position: relative;
@ -59,6 +60,9 @@ export default function Popups() {
// get all popups // get all popups
const activePopups = useActivePopups() const activePopups = useActivePopups()
// show survey popup if user has not closed
const [showSurveyPopup] = useShowSurveyPopup()
const urlWarningActive = useURLWarningVisible() const urlWarningActive = useURLWarningVisible()
// need extra padding if network is not L1 Ethereum // need extra padding if network is not L1 Ethereum
@ -69,12 +73,14 @@ export default function Popups() {
<> <>
<FixedPopupColumn gap="20px" extraPadding={urlWarningActive} xlPadding={isNotOnMainnet}> <FixedPopupColumn gap="20px" extraPadding={urlWarningActive} xlPadding={isNotOnMainnet}>
<ClaimPopup /> <ClaimPopup />
<SurveyPopup />
{activePopups.map((item) => ( {activePopups.map((item) => (
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} /> <PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />
))} ))}
</FixedPopupColumn> </FixedPopupColumn>
<MobilePopupWrapper height={activePopups?.length > 0 ? 'fit-content' : 0}> <MobilePopupWrapper height={activePopups?.length > 0 || showSurveyPopup ? 'fit-content' : 0}>
<MobilePopupInner> <MobilePopupInner>
<SurveyPopup />
{activePopups // reverse so new items up front {activePopups // reverse so new items up front
.slice(0) .slice(0)
.reverse() .reverse()

@ -18,6 +18,7 @@ export const updateMatchesDarkMode = createAction<{ matchesDarkMode: boolean }>(
export const updateUserDarkMode = createAction<{ userDarkMode: boolean }>('user/updateUserDarkMode') export const updateUserDarkMode = createAction<{ userDarkMode: boolean }>('user/updateUserDarkMode')
export const updateUserExpertMode = createAction<{ userExpertMode: boolean }>('user/updateUserExpertMode') export const updateUserExpertMode = createAction<{ userExpertMode: boolean }>('user/updateUserExpertMode')
export const updateUserLocale = createAction<{ userLocale: SupportedLocale }>('user/updateUserLocale') export const updateUserLocale = createAction<{ userLocale: SupportedLocale }>('user/updateUserLocale')
export const updateShowSurveyPopup = createAction<{ showSurveyPopup: boolean }>('user/updateShowSurveyPopup')
export const updateUserClientSideRouter = createAction<{ userClientSideRouter: boolean }>( export const updateUserClientSideRouter = createAction<{ userClientSideRouter: boolean }>(
'user/updateUserClientSideRouter' 'user/updateUserClientSideRouter'
) )

@ -20,6 +20,7 @@ import {
SerializedPair, SerializedPair,
SerializedToken, SerializedToken,
updateHideClosedPositions, updateHideClosedPositions,
updateShowSurveyPopup,
updateUserClientSideRouter, updateUserClientSideRouter,
updateUserDarkMode, updateUserDarkMode,
updateUserDeadline, updateUserDeadline,
@ -104,6 +105,18 @@ export function useExpertModeManager(): [boolean, () => void] {
return [expertMode, toggleSetExpertMode] return [expertMode, toggleSetExpertMode]
} }
export function useShowSurveyPopup(): [boolean | undefined, (showPopup: boolean) => void] {
const dispatch = useAppDispatch()
const showSurveyPopup = useAppSelector((state) => state.user.showSurveyPopup)
const toggleShowSurveyPopup = useCallback(
(showPopup: boolean) => {
dispatch(updateShowSurveyPopup({ showSurveyPopup: showPopup }))
},
[dispatch]
)
return [showSurveyPopup, toggleShowSurveyPopup]
}
export function useClientSideRouter(): [boolean, (userClientSideRouter: boolean) => void] { export function useClientSideRouter(): [boolean, (userClientSideRouter: boolean) => void] {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()

@ -12,6 +12,7 @@ import {
SerializedToken, SerializedToken,
updateHideClosedPositions, updateHideClosedPositions,
updateMatchesDarkMode, updateMatchesDarkMode,
updateShowSurveyPopup,
updateUserClientSideRouter, updateUserClientSideRouter,
updateUserDarkMode, updateUserDarkMode,
updateUserDeadline, updateUserDeadline,
@ -60,6 +61,9 @@ export interface UserState {
timestamp: number timestamp: number
URLWarningVisible: boolean URLWarningVisible: boolean
// undefined means has not gone through A/B split yet
showSurveyPopup: boolean | undefined
} }
function pairKey(token0Address: string, token1Address: string) { function pairKey(token0Address: string, token1Address: string) {
@ -80,6 +84,7 @@ export const initialState: UserState = {
pairs: {}, pairs: {},
timestamp: currentTimestamp(), timestamp: currentTimestamp(),
URLWarningVisible: true, URLWarningVisible: true,
showSurveyPopup: undefined,
} }
export default createReducer(initialState, (builder) => export default createReducer(initialState, (builder) =>
@ -147,6 +152,9 @@ export default createReducer(initialState, (builder) =>
.addCase(updateHideClosedPositions, (state, action) => { .addCase(updateHideClosedPositions, (state, action) => {
state.userHideClosedPositions = action.payload.userHideClosedPositions state.userHideClosedPositions = action.payload.userHideClosedPositions
}) })
.addCase(updateShowSurveyPopup, (state, action) => {
state.showSurveyPopup = action.payload.showSurveyPopup
})
.addCase(addSerializedToken, (state, { payload: { serializedToken } }) => { .addCase(addSerializedToken, (state, { payload: { serializedToken } }) => {
if (!state.tokens) { if (!state.tokens) {
state.tokens = {} state.tokens = {}