Skip to content
This repository has been archived by the owner on Jun 24, 2022. It is now read-only.

Commit

Permalink
Affiliate issues (#1714)
Browse files Browse the repository at this point in the history
* display a banner to the user when the eth address is invalid

* display a warning in Profile when not on mainnet

* keep referral query param when following own link

* hide close button for notification banner

* make profile data visible on mobile

* replace defaultProps with destructuring

* reset error on useEffect call
  • Loading branch information
ramirotw authored Nov 2, 2021
1 parent 192932e commit a34a0d8
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 19 deletions.
31 changes: 26 additions & 5 deletions src/custom/components/AffiliateStatusCheck/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, { useCallback, useEffect, useState } from 'react'
import { useHistory } from 'react-router-dom'
import { useHistory, useLocation } from 'react-router-dom'
import { useActiveWeb3React } from 'hooks/web3'
import NotificationBanner from 'components/NotificationBanner'
import { useReferralAddress, useUploadReferralDocAndSetDataHash } from 'state/affiliate/hooks'
import { useAppDispatch } from 'state/hooks'
import { hasTrades } from 'utils/trade'
import { retry, RetryOptions } from 'utils/retry'
import { SupportedChainId } from 'constants/chains'
import useParseReferralQueryParam from 'hooks/useParseReferralQueryParam'

type AffiliateStatus = 'NOT_CONNECTED' | 'OWN_LINK' | 'ALREADY_TRADED' | 'ACTIVE' | 'UNSUPPORTED_NETWORK'

Expand All @@ -26,17 +27,23 @@ export default function AffiliateStatusCheck() {
const appDispatch = useAppDispatch()
const uploadReferralDocAndSetDataHash = useUploadReferralDocAndSetDataHash()
const history = useHistory()
const location = useLocation()
const { account, chainId } = useActiveWeb3React()
const referralAddress = useReferralAddress()
const referralAddressQueryParam = useParseReferralQueryParam()
const [affiliateState, setAffiliateState] = useState<AffiliateStatus | null>()
const [error, setError] = useState('')

const uploadDataDoc = useCallback(async () => {
setError('')
if (!chainId || !account || !referralAddress) {
return
}

if (!referralAddress.isValid) {
setError('The referral address is invalid.')
return
}

try {
// we first validate that the user hasn't already traded
const userHasTrades = await retry(() => hasTrades(chainId, account), DEFAULT_RETRY_OPTIONS).promise
Expand All @@ -52,7 +59,7 @@ export default function AffiliateStatusCheck() {
}

try {
await retry(() => uploadReferralDocAndSetDataHash(referralAddress), DEFAULT_RETRY_OPTIONS).promise
await retry(() => uploadReferralDocAndSetDataHash(referralAddress.value), DEFAULT_RETRY_OPTIONS).promise

setAffiliateState('ACTIVE')
} catch (error) {
Expand All @@ -67,6 +74,7 @@ export default function AffiliateStatusCheck() {
}

setAffiliateState(null)
setError('')

if (!account) {
setAffiliateState('NOT_CONNECTED')
Expand All @@ -78,15 +86,28 @@ export default function AffiliateStatusCheck() {
return
}

if (referralAddress === account) {
if (referralAddress.value === account) {
// clean-up saved referral address if the user follows its own referral link
history.push('/profile')
setAffiliateState('OWN_LINK')

if (referralAddressQueryParam) {
history.push('/profile' + location.search)
}
return
}

uploadDataDoc()
}, [referralAddress, account, history, chainId, appDispatch, uploadDataDoc])
}, [
referralAddress,
account,
history,
chainId,
appDispatch,
uploadDataDoc,
location.search,
referralAddressQueryParam,
])

if (error) {
return (
Expand Down
5 changes: 4 additions & 1 deletion src/custom/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'
import { SupportedChainId as ChainId } from 'constants/chains'
import Web3Status from 'components/Web3Status'
import { ExternalLink } from 'theme'
import { stringify } from 'qs'

import HeaderMod, {
Title,
Expand Down Expand Up @@ -33,6 +34,7 @@ import { supportedChainId } from 'utils/supportedChainId'
import { formatSmart } from 'utils/format'
import NetworkCard, { NetworkInfo } from './NetworkCard'
import SVG from 'react-inlinesvg'
import useParsedQueryString from 'hooks/useParsedQueryString'

// Halloween temporary
import SpiderRag from 'assets/cow-swap/halloween-spider.svg'
Expand Down Expand Up @@ -225,6 +227,7 @@ export default function Header() {
const closeOrdersPanel = () => setIsOrdersPanelOpen(false)
const openOrdersPanel = () => setIsOrdersPanelOpen(true)
const isMenuOpen = useModalOpen(ApplicationModal.MENU)
const parsedQs = useParsedQueryString()

// Toggle the 'noScroll' class on body, whenever the orders panel or flyout menu is open.
// This removes the inner scrollbar on the page body, to prevent showing double scrollbars.
Expand All @@ -238,7 +241,7 @@ export default function Header() {
<Wrapper isDarkMode={darkMode}>
<HeaderModWrapper>
<HeaderRow marginRight="0">
<Title href=".">
<Title href={'./#/?' + stringify(parsedQs)}>
<UniIcon>
<LogoImage />
</UniIcon>
Expand Down
5 changes: 4 additions & 1 deletion src/custom/components/NotificationBanner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface BannerProps {
children: React.ReactNode
level: Level
isVisible: boolean
canClose?: boolean
}

const Banner = styled.div<Pick<BannerProps, 'isVisible' | 'level'>>`
Expand Down Expand Up @@ -41,10 +42,12 @@ const BannerContainer = styled.div`
`
export default function NotificationBanner(props: BannerProps) {
const [isActive, setIsActive] = useState(props.isVisible)
const { canClose = true } = props

return (
<Banner {...props} isVisible={isActive}>
<BannerContainer>{props.children}</BannerContainer>
<StyledClose size={16} onClick={() => setIsActive(false)} />
{canClose && <StyledClose size={16} onClick={() => setIsActive(false)} />}
</Banner>
)
}
10 changes: 8 additions & 2 deletions src/custom/hooks/useParseReferralQueryParam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,26 @@ import { isAddress } from 'ethers/lib/utils'
import useParsedQueryString from 'hooks/useParsedQueryString'
import { REFERRAL_QUERY_PARAM } from 'hooks/useReferralLink'

type ReferralQueryValue = {
value: string
isValid: boolean
} | null

/**
* Returns the parsed referral address from the query parameters if its a valid address
*/
export default function useParseReferralQueryParam(): string | null {
export default function useParseReferralQueryParam(): ReferralQueryValue {
const parsedQs = useParsedQueryString()

const referral = useMemo(() => {
const referralAddress = parsedQs[REFERRAL_QUERY_PARAM]
if (typeof referralAddress === 'string' && isAddress(referralAddress)) {
return referralAddress
return { value: referralAddress, isValid: true }
}

if (referralAddress) {
console.warn('Invalid referral address')
return { value: '', isValid: false }
}

return null
Expand Down
9 changes: 8 additions & 1 deletion src/custom/pages/Profile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ import useFetchProfile from 'hooks/useFetchProfile'
import { numberFormatter } from 'utils/format'
import useTimeAgo from 'hooks/useTimeAgo'
import { MouseoverTooltipContent } from 'components/Tooltip'
import NotificationBanner from 'components/NotificationBanner'
import { SupportedChainId as ChainId } from 'constants/chains'

export default function Profile() {
const referralLink = useReferralLink()
const { account } = useActiveWeb3React()
const { account, chainId } = useActiveWeb3React()
const profileData = useFetchProfile()
const lastUpdated = useTimeAgo(profileData?.lastUpdated)

Expand Down Expand Up @@ -52,6 +54,11 @@ export default function Profile() {
</Txt>
)}
</CardHead>
{chainId && chainId !== ChainId.MAINNET && (
<NotificationBanner isVisible level="info" canClose={false}>
Profile data is only available for mainnet. Please change the network to see it.
</NotificationBanner>
)}
<ChildWrapper>
<Txt fs={16}>
<strong>Your referral url</strong>
Expand Down
3 changes: 0 additions & 3 deletions src/custom/pages/Profile/styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,6 @@ export const GridWrap = styled.div<Partial<CSS.Properties & { horizontal?: boole
grid-column-start: 1;
grid-column-end: 2;
}
> :nth-child(4) {
display: none;
}
`};
`

Expand Down
5 changes: 4 additions & 1 deletion src/custom/state/affiliate/actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { createAction } from '@reduxjs/toolkit'

export const updateReferralAddress = createAction<string>('affiliate/updateReferralAddress')
export const updateReferralAddress = createAction<{
value: string
isValid: boolean
} | null>('affiliate/updateReferralAddress')
export const updateAppDataHash = createAction<string>('affiliate/updateAppDataHash')
11 changes: 9 additions & 2 deletions src/custom/state/affiliate/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,22 @@ export function useAppDataHash() {
}

export function useReferralAddress() {
return useSelector<AppState, string | undefined>((state) => {
return useSelector<
AppState,
| {
value: string
isValid: boolean
}
| undefined
>((state) => {
return state.affiliate.referralAddress
})
}

export function useResetReferralAddress() {
const dispatch = useAppDispatch()

return useCallback(() => dispatch(updateReferralAddress('')), [dispatch])
return useCallback(() => dispatch(updateReferralAddress(null)), [dispatch])
}

export function useUploadReferralDocAndSetDataHash() {
Expand Down
8 changes: 5 additions & 3 deletions src/custom/state/affiliate/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ import { updateAppDataHash, updateReferralAddress } from './actions'
import { APP_DATA_HASH } from 'constants/index'

export interface AffiliateState {
referralAddress?: string
referralAddress?: {
value: string
isValid: boolean
}
appDataHash: string
}

export const initialState: AffiliateState = {
referralAddress: '',
appDataHash: APP_DATA_HASH,
}

export default createReducer(initialState, (builder) =>
builder
.addCase(updateReferralAddress, (state, action) => {
state.referralAddress = action.payload
state.referralAddress = action.payload ?? undefined
})
.addCase(updateAppDataHash, (state, action) => {
state.appDataHash = action.payload
Expand Down

0 comments on commit a34a0d8

Please sign in to comment.