Skip to content

Commit

Permalink
Sort ads by targeting score
Browse files Browse the repository at this point in the history
  • Loading branch information
aidenaio committed Sep 14, 2022
1 parent 5f208b3 commit a27028b
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 52 deletions.
59 changes: 54 additions & 5 deletions renderer/pages/adn/offers.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import {Center, Table, Tbody, Thead, Tr, useDisclosure} from '@chakra-ui/react'
import {
Center,
Table,
Tbody,
Text,
Thead,
Tr,
useDisclosure,
} from '@chakra-ui/react'
import React from 'react'
import {useTranslation} from 'react-i18next'
import {useQueryClient} from 'react-query'
import {useApprovedBurntCoins} from '../../screens/ads/hooks'
import {
useApprovedBurntCoins,
useProtoProfileDecoder,
} from '../../screens/ads/hooks'
import Layout from '../../shared/components/layout'
import {PageHeader, PageCloseButton} from '../../screens/ads/components'
import {AdOfferListItem, BurnDrawer} from '../../screens/ads/containers'
import {Page, PageTitle, RoundedTh} from '../../shared/components/components'
import {
Page,
PageTitle,
RoundedTh,
Tooltip,
} from '../../shared/components/components'
import {InfoIcon} from '../../shared/components/icons'
import {calculateTotalAdScore} from '../../screens/ads/utils'

export default function AdOfferList() {
const {t} = useTranslation()
Expand All @@ -15,6 +33,19 @@ export default function AdOfferList() {

const {data: burntCoins, status: burntCoinsStatus} = useApprovedBurntCoins()

const {decodeAdTarget} = useProtoProfileDecoder()

const orderedBurntCoins = React.useMemo(
() =>
burntCoins
.map(({target, ...burn}) => ({
...burn,
totalScore: calculateTotalAdScore(decodeAdTarget(target)),
}))
.sort((a, b) => b.totalScore - a.totalScore),
[burntCoins, decodeAdTarget]
)

const isFetched = burntCoinsStatus === 'success'

const isEmpty = isFetched && burntCoins.length === 0
Expand Down Expand Up @@ -53,14 +84,32 @@ export default function AdOfferList() {
<Tr>
<RoundedTh isLeft>{t('Banner/author')}</RoundedTh>
<RoundedTh>{t('Website')}</RoundedTh>
<RoundedTh>{t('Target')}</RoundedTh>
<RoundedTh>
<Text w="16">
{t('Targeting coeff')}{' '}
<Tooltip
label="Coeff = iif(language, 22, 1) * iif(os, 5, 1)"
placement="top"
>
<InfoIcon cursor="help" flex={1} />
</Tooltip>
</Text>
</RoundedTh>
<RoundedTh>{t('Burn')}</RoundedTh>
<RoundedTh cursor="help">
<Text w="14">
{t('Total score')}{' '}
<Tooltip label="Total score = burn * coeff" placement="top">
<InfoIcon cursor="help" />
</Tooltip>
</Text>
</RoundedTh>
<RoundedTh isRight />
</Tr>
</Thead>
<Tbody>
{isFetched &&
burntCoins.map(burn => (
orderedBurntCoins.map(burn => (
<AdOfferListItem
key={burn.key}
burn={burn}
Expand Down
128 changes: 84 additions & 44 deletions renderer/screens/ads/containers.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import {
AdsIcon,
ArrowLeftIcon,
ArrowRightIcon,
ChevronRightIcon,
DeleteIcon,
EditIcon,
EyeIcon,
Expand All @@ -107,6 +108,9 @@ import {AVAILABLE_LANGS} from '../../i18n'
import {
adFallbackSrc,
adImageThumbSrc,
calculateTargetParamWeight,
calculateTargetScore,
calculateTotalAdScore,
compressAdImage,
isValidImage,
OS,
Expand Down Expand Up @@ -502,9 +506,9 @@ function AdPromotion({cid, title, desc, url, media, author}) {
rounded="lg"
px="10"
py="4"
// pb="10"
// w="sm"
h="xl"
pb="10"
w="mdx"
h="620px"
>
<Stack spacing="4">
<Stack spacing="2">
Expand All @@ -518,15 +522,22 @@ function AdPromotion({cid, title, desc, url, media, author}) {
fontWeight="semibold"
fontSize="md"
lineHeight="5"
isTruncated
maxW="80"
noOfLines={1}
>
{title}
</Heading>
</Skeleton>

<Stack spacing="1.5" minH={62}>
<Skeleton isLoaded={Boolean(desc)} minH={5}>
<Text color="muted" fontSize="md" lineHeight="5">
<Text
color="muted"
fontSize="md"
lineHeight="5"
maxW="80"
noOfLines={2}
>
{desc}
</Text>
</Skeleton>
Expand All @@ -552,7 +563,7 @@ function AdPromotion({cid, title, desc, url, media, author}) {
global.openExternal(url)
}}
>
<AdImage src={media} w="280px" />
<AdImage src={media} w="80" />
</LinkOverlay>
</LinkBox>
</Stack>
Expand Down Expand Up @@ -1731,6 +1742,8 @@ export function AdOfferListItem({
fontSize="sm"
fontWeight={500}
lineHeight="shorter"
w="32"
noOfLines={1}
>
{ad.author}
</Text>
Expand All @@ -1739,54 +1752,81 @@ export function AdOfferListItem({
</HStack>
</Td>
<Td borderColor="gray.300">
<Link target="_blank" color="blue.500">
<Link target="_blank" color="blue.500" maxW="40" noOfLines={2}>
{ad.url}
</Link>
</Td>
<Td borderColor="gray.300">
{targetValues.some(Boolean) ? (
<>
{t('Set')}{' '}
<Tooltip
label={
<InlineAdStatGroup labelWidth="16">
<SmallInlineAdStat
label={t('Language')}
value={ad.language || 'Any'}
/>
<SmallInlineAdStat label={t('OS')} value={ad.os || 'Any'} />
<SmallInlineAdStat label={t('Age')} value={ad.age ?? 'Any'} />
<SmallInlineAdStat
label={t('Stake')}
value={ad.stake ?? 'Any'}
/>
</InlineAdStatGroup>
}
placement="right-start"
arrowSize={8}
offset={[-8, 6]}
>
<Button
variant="link"
rightIcon={<ChevronRightIcon />}
iconSpacing="0"
color="blue.500"
cursor="pointer"
_hover={{
textDecoration: 'none',
}}
<Stack spacing="1">
<Text>{calculateTargetScore(ad)}</Text>
<HStack align="baseline" spacing="1">
<Text>{t('Set')}</Text>
<Tooltip
label={
<InlineAdStatGroup labelWidth="16">
<SmallInlineAdStat
label={t('Language')}
value={`${ad.language ||
'Any'} (${calculateTargetParamWeight(
ad.language,
22
)})`}
/>
<SmallInlineAdStat
label={t('OS')}
value={`${ad.os || 'Any'} (${calculateTargetParamWeight(
ad.os,
5
)})`}
/>
<SmallInlineAdStat
label={t('Age')}
value={ad.age ?? 'Any'}
/>
<SmallInlineAdStat
label={t('Stake')}
value={ad.stake ?? 'Any'}
/>
</InlineAdStatGroup>
}
placement="right-start"
arrowSize={8}
offset={[-8, 6]}
>
{t('{{count}} targets', {
count: targetValues.filter(Boolean).length,
})}
</Button>
</Tooltip>
</>
<Button
variant="link"
rightIcon={<ChevronRightIcon />}
iconSpacing="0"
color="blue.500"
cursor="pointer"
_hover={{
textDecoration: 'none',
}}
>
{t('{{count}} targets', {
count: targetValues.filter(Boolean).length,
})}
</Button>
</Tooltip>
</HStack>
</Stack>
) : (
t('Not set')
<Stack spacing="1">
<Text>
<Text>{calculateTargetScore(ad)}</Text>
</Text>
<Text>{t('Not set')}</Text>
</Stack>
)}
</Td>
<Td borderColor="gray.300">{formatDna(amount)}</Td>
<Td borderColor="gray.300">
{calculateTotalAdScore({
target: decodeAdTarget(target),
burnAmount: amount,
})}
</Td>
<Td borderColor="gray.300">
{isSelfAuthor ? (
<SecondaryButton
Expand Down
25 changes: 22 additions & 3 deletions renderer/screens/ads/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
adVotingDefaults,
isTargetedAd,
sendTx,
calculateTotalAdScore,
} from './utils'
import {TxType} from '../../shared/types'
import {capitalize} from '../../shared/utils/string'
Expand Down Expand Up @@ -67,7 +68,7 @@ export function useRotatingAds(limit = 3) {
}))
)

const {decodeAd, decodeProfile} = useProtoProfileDecoder()
const {decodeAd, decodeProfile, decodeAdTarget} = useProtoProfileDecoder()

const profiles = useQueries(
profileHashes.map(({data: cid}) => ({
Expand Down Expand Up @@ -106,7 +107,6 @@ export function useRotatingAds(limit = 3) {
({data}) => data?.cid === AdBurnKey.fromHex(key).cid
)
)
.slice(0, limit)
.map(({key, address, amount}) => {
const {cid} = AdBurnKey.fromHex(key)
return {
Expand All @@ -131,7 +131,26 @@ export function useRotatingAds(limit = 3) {
}) ?? []
)

return decodedProfileAds?.map(x => x.data).filter(Boolean) ?? []
return (
decodedProfileAds
?.map(({data}) => {
if (data) {
const burn = burntCoins.find(({cid}) => cid === data.cid)

if (burn) {
return {
...data,
totalScore: calculateTotalAdScore(decodeAdTarget(burn.target)),
}
}

return data
}
})
.filter(Boolean) ?? []
)
.sort((a, b) => b.totalScore - a.totalScore)
.slice(0, limit)
}

export function useCurrentBannerAd() {
Expand Down
14 changes: 14 additions & 0 deletions renderer/screens/ads/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,17 @@ export const validateAdVoting = ({ad, voting}) => {

return false
}

export function calculateTotalAdScore({target, burnAmount}) {
return burnAmount * calculateTargetScore(target)
}

export function calculateTargetScore(target) {
return (
calculateTargetParamWeight(target?.language, 22) *
calculateTargetParamWeight(target?.os, 5)
)
}

export const calculateTargetParamWeight = (param, weight) =>
param ? weight : 1
1 change: 1 addition & 0 deletions renderer/shared/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export const theme = {
},
sizes: {
sm: rem(360),
mdx: '400px',
md: rem(480),
},
radii: {
Expand Down

0 comments on commit a27028b

Please sign in to comment.