feat: Collection page log events (#4971)

* Log collection page view

* Make property names/intention more clear

* Remove console log

* Add event for Activity tab

* Filter events (buy now, marketplaces, and price range)

* Handle trait items

* Bump collection stats mobile padding

* Use shouldLogImpression to conditionally fire event

* Adding back trace on Collection page, still necessary

* Add back address property

* Drop Buy Now log, not part of first set

* Update filter properties to match spreadsheet

* Only trigger price range log if inputs contain a value

* Fix ordering on Page Names

* Capitalize text

* Add constant for filter types

* Add chainId as property
This commit is contained in:
Greg Bugyis 2022-10-27 00:44:46 +03:00 committed by GitHub
parent e2fea4a5fb
commit d86120a257
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 134 additions and 78 deletions

@ -14,6 +14,9 @@ export enum EventName {
PAGE_VIEWED = 'Page Viewed',
NAVBAR_SEARCH_SELECTED = 'Navbar Search Selected',
NAVBAR_SEARCH_EXITED = 'Navbar Search Exited',
NFT_ACTIVITY_SELECTED = 'NFT Activity Selected',
NFT_FILTER_OPENED = 'NFT Collection Filter Opened',
NFT_FILTER_SELECTED = 'NFT Filter Selected',
SWAP_AUTOROUTER_VISUALIZATION_EXPANDED = 'Swap Autorouter Visualization Expanded',
SWAP_DETAILS_EXPANDED = 'Swap Details Expanded',
SWAP_MAX_TOKEN_AMOUNT_SELECTED = 'Swap Max Token Amount Selected',
@ -74,6 +77,7 @@ export enum SWAP_PRICE_UPDATE_USER_RESPONSE {
* Known pages in the app. Highest order context.
*/
export enum PageName {
NFT_COLLECTION_PAGE = 'nft-collection-page',
NFT_DETAILS_PAGE = 'nft-details-page',
TOKEN_DETAILS_PAGE = 'token-details',
TOKENS_PAGE = 'tokens-page',
@ -116,6 +120,9 @@ export enum ElementName {
IMPORT_TOKEN_BUTTON = 'import-token-button',
MAX_TOKEN_AMOUNT_BUTTON = 'max-token-amount-button',
NAVBAR_SEARCH_INPUT = 'navbar-search-input',
NFT_ACTIVITY_TAB = 'nft-activity-tab',
NFT_FILTER_BUTTON = 'nft-filter-button',
NFT_FILTER_OPTION = 'nft-filter-option',
PRICE_UPDATE_ACCEPT_BUTTON = 'price-update-accept-button',
SWAP_BUTTON = 'swap-button',
SWAP_DETAILS_DROPDOWN = 'swap-details-dropdown',
@ -137,3 +144,12 @@ export enum Event {
onSelect = 'onSelect',
// alphabetize additional events.
}
/**
* Known Filter Types for NFTs
*/
export enum FilterTypes {
MARKETPLACE = 'Marketplace',
PRICE_RANGE = 'Price Range',
TRAIT = 'Trait',
}

@ -1,3 +1,5 @@
import { ElementName, Event, EventName } from 'analytics/constants'
import { TraceEvent } from 'analytics/TraceEvent'
import { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex'
import { useIsCollectionLoading } from 'nft/hooks'
@ -27,13 +29,19 @@ export const ActivitySwitcher = ({
>
Items
</Box>
<Box
as="button"
className={!showActivity ? styles.activitySwitcherToggle : styles.selectedActivitySwitcherToggle}
onClick={() => !showActivity && toggleActivity()}
<TraceEvent
events={[Event.onClick]}
element={ElementName.NFT_ACTIVITY_TAB}
name={EventName.NFT_ACTIVITY_SELECTED}
>
Activity
</Box>
<Box
as="button"
className={!showActivity ? styles.activitySwitcherToggle : styles.selectedActivitySwitcherToggle}
onClick={() => !showActivity && toggleActivity()}
>
Activity
</Box>
</TraceEvent>
</>
)}
</Row>

@ -1,3 +1,6 @@
import { useWeb3React } from '@web3-react/core'
import { ElementName, Event, EventName } from 'analytics/constants'
import { TraceEvent } from 'analytics/TraceEvent'
import clsx from 'clsx'
import { loadingAnimation } from 'components/Loader/styled'
import useDebounce from 'hooks/useDebounce'
@ -104,6 +107,7 @@ export const LoadingButton = styled.div`
`
export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerified }: CollectionNftsProps) => {
const { chainId } = useWeb3React()
const traits = useCollectionFilters((state) => state.traits)
const minPrice = useCollectionFilters((state) => state.minPrice)
const maxPrice = useCollectionFilters((state) => state.maxPrice)
@ -356,12 +360,20 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
<Box backgroundColor="backgroundFloating" width="full" style={{ backdropFilter: 'blur(24px)' }}>
<ActionsContainer>
<Row gap="12">
<FilterButton
isMobile={isMobile}
isFiltersExpanded={isFiltersExpanded}
onClick={() => setFiltersExpanded(!isFiltersExpanded)}
collectionCount={collectionNfts?.[0]?.totalCount ?? 0}
/>
<TraceEvent
events={[Event.onClick]}
element={ElementName.NFT_FILTER_BUTTON}
name={EventName.NFT_FILTER_OPENED}
shouldLogImpression={!isFiltersExpanded}
properties={{ collection_address: contractAddress, chain_id: chainId }}
>
<FilterButton
isMobile={isMobile}
isFiltersExpanded={isFiltersExpanded}
onClick={() => setFiltersExpanded(!isFiltersExpanded)}
collectionCount={collectionNfts?.[0]?.totalCount ?? 0}
/>
</TraceEvent>
<SortDropdown dropDownOptions={sortDropDownOptions} />
<CollectionSearch />
</Row>

@ -277,7 +277,7 @@ const StatsRow = ({ stats, isMobile, ...props }: { stats: GenieCollection; isMob
)
return (
<Row gap={{ sm: '20', md: '60' }} {...props}>
<Row gap={{ sm: '36', md: '60' }} {...props}>
{isCollectionStatsLoading && statsLoadingSkeleton}
{stats.floorPrice ? (
<StatsItem label="Global floor" isMobile={isMobile ?? false}>

@ -1,3 +1,5 @@
import { sendAnalyticsEvent } from 'analytics'
import { EventName, FilterTypes } from 'analytics/constants'
import clsx from 'clsx'
import { Box } from 'nft/components/Box'
import * as styles from 'nft/components/collection/Filters.css'
@ -48,6 +50,7 @@ const MarketplaceItem = ({
removeMarket(value)
setCheckboxSelected(false)
}
sendAnalyticsEvent(EventName.NFT_FILTER_SELECTED, { filter_type: FilterTypes.MARKETPLACE })
}
return (

@ -1,5 +1,7 @@
import 'rc-slider/assets/index.css'
import { sendAnalyticsEvent } from 'analytics'
import { EventName, FilterTypes } from 'analytics/constants'
import { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex'
import { NumericInput } from 'nft/components/layout/Input'
@ -54,6 +56,8 @@ export const PriceRange = () => {
const handleBlur: FocusEventHandler<HTMLInputElement> = (e) => {
e.currentTarget.placeholder = placeholderText
setPlaceholderText('')
if (minPrice || maxPrice)
sendAnalyticsEvent(EventName.NFT_FILTER_SELECTED, { filter_type: FilterTypes.PRICE_RANGE })
}
const updateMinPriceRange = (v: FormEvent<HTMLInputElement>) => {

@ -1,3 +1,5 @@
import { sendAnalyticsEvent } from 'analytics'
import { EventName, FilterTypes } from 'analytics/constants'
import useDebounce from 'hooks/useDebounce'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
@ -53,6 +55,7 @@ const TraitItem = ({
removeTrait(trait)
setCheckboxSelected(false)
}
sendAnalyticsEvent(EventName.NFT_FILTER_SELECTED, { filter_type: FilterTypes.TRAIT })
}
const showFullTraitName = shouldShow && trait_type === trait.trait_type && trait_value === trait.trait_value

@ -1,3 +1,6 @@
import { useWeb3React } from '@web3-react/core'
import { PageName } from 'analytics/constants'
import { Trace } from 'analytics/Trace'
import { MobileHoverBag } from 'nft/components/bag/MobileHoverBag'
import { AnimatedBox, Box } from 'nft/components/Box'
import { Activity, ActivitySwitcher, CollectionNfts, CollectionStats, Filters } from 'nft/components/collection'
@ -25,6 +28,7 @@ const Collection = () => {
const isActivityToggled = pathname.includes('/activity')
const setMarketCount = useCollectionFilters((state) => state.setMarketCount)
const isBagExpanded = useBag((state) => state.bagExpanded)
const { chainId } = useWeb3React()
const { data: collectionStats, isLoading } = useQuery(['collectionStats', contractAddress], () =>
CollectionStatsFetcher(contractAddress as string)
@ -61,75 +65,81 @@ const Collection = () => {
return (
<>
<Column width="full">
{contractAddress ? (
<>
{' '}
<Box width="full" height="160">
<Trace
page={PageName.NFT_COLLECTION_PAGE}
properties={{ collection_address: contractAddress, chain_id: chainId }}
shouldLogImpression
>
<Column width="full">
{contractAddress ? (
<>
{' '}
<Box width="full" height="160">
{isLoading ? (
<Box height="full" width="full" className={styles.loadingBanner} />
) : (
<Box
as="img"
height="full"
width="full"
src={collectionStats?.bannerImageUrl}
className={isLoading ? styles.loadingBanner : styles.bannerImage}
background="none"
/>
<Box width="full" height="160">
{isLoading ? (
<Box height="full" width="full" className={styles.loadingBanner} />
) : (
<Box
as="img"
height="full"
width="full"
src={collectionStats?.bannerImageUrl}
className={isLoading ? styles.loadingBanner : styles.bannerImage}
background="none"
/>
)}
</Box>
</Box>
<Column paddingX="32">
{(isLoading || collectionStats !== undefined) && (
<CollectionStats stats={collectionStats || ({} as GenieCollection)} isMobile={isMobile} />
)}
</Box>
</Box>
<Column paddingX="32">
{(isLoading || collectionStats !== undefined) && (
<CollectionStats stats={collectionStats || ({} as GenieCollection)} isMobile={isMobile} />
)}
<ActivitySwitcher
showActivity={isActivityToggled}
toggleActivity={() => {
isFiltersExpanded && setFiltersExpanded(false)
toggleActivity()
}}
/>
</Column>
<Row alignItems="flex-start" position="relative" paddingX="48">
<Box position="sticky" top="72" width="0">
{isFiltersExpanded && <Filters traits={collectionStats?.traits ?? []} />}
</Box>
<ActivitySwitcher
showActivity={isActivityToggled}
toggleActivity={() => {
isFiltersExpanded && setFiltersExpanded(false)
toggleActivity()
}}
/>
</Column>
<Row alignItems="flex-start" position="relative" paddingX="48">
<Box position="sticky" top="72" width="0">
{isFiltersExpanded && <Filters traits={collectionStats?.traits ?? []} />}
</Box>
{/* @ts-ignore: https://github.com/microsoft/TypeScript/issues/34933 */}
<AnimatedBox
style={{
transform: gridX.to((x) => `translate(${x as number}px)`),
width: gridWidthOffset.to((x) => `calc(100% - ${x as number}px)`),
}}
>
{isActivityToggled
? contractAddress && (
<Activity
contractAddress={contractAddress}
rarityVerified={collectionStats?.rarityVerified ?? false}
collectionName={collectionStats?.name ?? ''}
/>
)
: contractAddress &&
(isLoading || collectionStats !== undefined) && (
<CollectionNfts
collectionStats={collectionStats || ({} as GenieCollection)}
contractAddress={contractAddress}
rarityVerified={collectionStats?.rarityVerified}
/>
)}
</AnimatedBox>
</Row>
</>
) : (
// TODO: Put no collection asset page here
!isLoading && <div className={styles.noCollectionAssets}>No collection assets exist at this address</div>
)}
</Column>
{/* @ts-ignore: https://github.com/microsoft/TypeScript/issues/34933 */}
<AnimatedBox
style={{
transform: gridX.to((x) => `translate(${x as number}px)`),
width: gridWidthOffset.to((x) => `calc(100% - ${x as number}px)`),
}}
>
{isActivityToggled
? contractAddress && (
<Activity
contractAddress={contractAddress}
rarityVerified={collectionStats?.rarityVerified ?? false}
collectionName={collectionStats?.name ?? ''}
/>
)
: contractAddress &&
(isLoading || collectionStats !== undefined) && (
<CollectionNfts
collectionStats={collectionStats || ({} as GenieCollection)}
contractAddress={contractAddress}
rarityVerified={collectionStats?.rarityVerified}
/>
)}
</AnimatedBox>
</Row>
</>
) : (
// TODO: Put no collection asset page here
!isLoading && <div className={styles.noCollectionAssets}>No collection assets exist at this address</div>
)}
</Column>
</Trace>
<MobileHoverBag />
</>
)