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', PAGE_VIEWED = 'Page Viewed',
NAVBAR_SEARCH_SELECTED = 'Navbar Search Selected', NAVBAR_SEARCH_SELECTED = 'Navbar Search Selected',
NAVBAR_SEARCH_EXITED = 'Navbar Search Exited', 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_AUTOROUTER_VISUALIZATION_EXPANDED = 'Swap Autorouter Visualization Expanded',
SWAP_DETAILS_EXPANDED = 'Swap Details Expanded', SWAP_DETAILS_EXPANDED = 'Swap Details Expanded',
SWAP_MAX_TOKEN_AMOUNT_SELECTED = 'Swap Max Token Amount Selected', 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. * Known pages in the app. Highest order context.
*/ */
export enum PageName { export enum PageName {
NFT_COLLECTION_PAGE = 'nft-collection-page',
NFT_DETAILS_PAGE = 'nft-details-page', NFT_DETAILS_PAGE = 'nft-details-page',
TOKEN_DETAILS_PAGE = 'token-details', TOKEN_DETAILS_PAGE = 'token-details',
TOKENS_PAGE = 'tokens-page', TOKENS_PAGE = 'tokens-page',
@ -116,6 +120,9 @@ export enum ElementName {
IMPORT_TOKEN_BUTTON = 'import-token-button', IMPORT_TOKEN_BUTTON = 'import-token-button',
MAX_TOKEN_AMOUNT_BUTTON = 'max-token-amount-button', MAX_TOKEN_AMOUNT_BUTTON = 'max-token-amount-button',
NAVBAR_SEARCH_INPUT = 'navbar-search-input', 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', PRICE_UPDATE_ACCEPT_BUTTON = 'price-update-accept-button',
SWAP_BUTTON = 'swap-button', SWAP_BUTTON = 'swap-button',
SWAP_DETAILS_DROPDOWN = 'swap-details-dropdown', SWAP_DETAILS_DROPDOWN = 'swap-details-dropdown',
@ -137,3 +144,12 @@ export enum Event {
onSelect = 'onSelect', onSelect = 'onSelect',
// alphabetize additional events. // 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 { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex' import { Row } from 'nft/components/Flex'
import { useIsCollectionLoading } from 'nft/hooks' import { useIsCollectionLoading } from 'nft/hooks'
@ -27,6 +29,11 @@ export const ActivitySwitcher = ({
> >
Items Items
</Box> </Box>
<TraceEvent
events={[Event.onClick]}
element={ElementName.NFT_ACTIVITY_TAB}
name={EventName.NFT_ACTIVITY_SELECTED}
>
<Box <Box
as="button" as="button"
className={!showActivity ? styles.activitySwitcherToggle : styles.selectedActivitySwitcherToggle} className={!showActivity ? styles.activitySwitcherToggle : styles.selectedActivitySwitcherToggle}
@ -34,6 +41,7 @@ export const ActivitySwitcher = ({
> >
Activity Activity
</Box> </Box>
</TraceEvent>
</> </>
)} )}
</Row> </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 clsx from 'clsx'
import { loadingAnimation } from 'components/Loader/styled' import { loadingAnimation } from 'components/Loader/styled'
import useDebounce from 'hooks/useDebounce' import useDebounce from 'hooks/useDebounce'
@ -104,6 +107,7 @@ export const LoadingButton = styled.div`
` `
export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerified }: CollectionNftsProps) => { export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerified }: CollectionNftsProps) => {
const { chainId } = useWeb3React()
const traits = useCollectionFilters((state) => state.traits) const traits = useCollectionFilters((state) => state.traits)
const minPrice = useCollectionFilters((state) => state.minPrice) const minPrice = useCollectionFilters((state) => state.minPrice)
const maxPrice = useCollectionFilters((state) => state.maxPrice) 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)' }}> <Box backgroundColor="backgroundFloating" width="full" style={{ backdropFilter: 'blur(24px)' }}>
<ActionsContainer> <ActionsContainer>
<Row gap="12"> <Row gap="12">
<TraceEvent
events={[Event.onClick]}
element={ElementName.NFT_FILTER_BUTTON}
name={EventName.NFT_FILTER_OPENED}
shouldLogImpression={!isFiltersExpanded}
properties={{ collection_address: contractAddress, chain_id: chainId }}
>
<FilterButton <FilterButton
isMobile={isMobile} isMobile={isMobile}
isFiltersExpanded={isFiltersExpanded} isFiltersExpanded={isFiltersExpanded}
onClick={() => setFiltersExpanded(!isFiltersExpanded)} onClick={() => setFiltersExpanded(!isFiltersExpanded)}
collectionCount={collectionNfts?.[0]?.totalCount ?? 0} collectionCount={collectionNfts?.[0]?.totalCount ?? 0}
/> />
</TraceEvent>
<SortDropdown dropDownOptions={sortDropDownOptions} /> <SortDropdown dropDownOptions={sortDropDownOptions} />
<CollectionSearch /> <CollectionSearch />
</Row> </Row>

@ -277,7 +277,7 @@ const StatsRow = ({ stats, isMobile, ...props }: { stats: GenieCollection; isMob
) )
return ( return (
<Row gap={{ sm: '20', md: '60' }} {...props}> <Row gap={{ sm: '36', md: '60' }} {...props}>
{isCollectionStatsLoading && statsLoadingSkeleton} {isCollectionStatsLoading && statsLoadingSkeleton}
{stats.floorPrice ? ( {stats.floorPrice ? (
<StatsItem label="Global floor" isMobile={isMobile ?? false}> <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 clsx from 'clsx'
import { Box } from 'nft/components/Box' import { Box } from 'nft/components/Box'
import * as styles from 'nft/components/collection/Filters.css' import * as styles from 'nft/components/collection/Filters.css'
@ -48,6 +50,7 @@ const MarketplaceItem = ({
removeMarket(value) removeMarket(value)
setCheckboxSelected(false) setCheckboxSelected(false)
} }
sendAnalyticsEvent(EventName.NFT_FILTER_SELECTED, { filter_type: FilterTypes.MARKETPLACE })
} }
return ( return (

@ -1,5 +1,7 @@
import 'rc-slider/assets/index.css' import 'rc-slider/assets/index.css'
import { sendAnalyticsEvent } from 'analytics'
import { EventName, FilterTypes } from 'analytics/constants'
import { Box } from 'nft/components/Box' import { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex' import { Row } from 'nft/components/Flex'
import { NumericInput } from 'nft/components/layout/Input' import { NumericInput } from 'nft/components/layout/Input'
@ -54,6 +56,8 @@ export const PriceRange = () => {
const handleBlur: FocusEventHandler<HTMLInputElement> = (e) => { const handleBlur: FocusEventHandler<HTMLInputElement> = (e) => {
e.currentTarget.placeholder = placeholderText e.currentTarget.placeholder = placeholderText
setPlaceholderText('') setPlaceholderText('')
if (minPrice || maxPrice)
sendAnalyticsEvent(EventName.NFT_FILTER_SELECTED, { filter_type: FilterTypes.PRICE_RANGE })
} }
const updateMinPriceRange = (v: FormEvent<HTMLInputElement>) => { 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 useDebounce from 'hooks/useDebounce'
import { Box } from 'nft/components/Box' import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex' import { Column, Row } from 'nft/components/Flex'
@ -53,6 +55,7 @@ const TraitItem = ({
removeTrait(trait) removeTrait(trait)
setCheckboxSelected(false) setCheckboxSelected(false)
} }
sendAnalyticsEvent(EventName.NFT_FILTER_SELECTED, { filter_type: FilterTypes.TRAIT })
} }
const showFullTraitName = shouldShow && trait_type === trait.trait_type && trait_value === trait.trait_value 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 { MobileHoverBag } from 'nft/components/bag/MobileHoverBag'
import { AnimatedBox, Box } from 'nft/components/Box' import { AnimatedBox, Box } from 'nft/components/Box'
import { Activity, ActivitySwitcher, CollectionNfts, CollectionStats, Filters } from 'nft/components/collection' import { Activity, ActivitySwitcher, CollectionNfts, CollectionStats, Filters } from 'nft/components/collection'
@ -25,6 +28,7 @@ const Collection = () => {
const isActivityToggled = pathname.includes('/activity') const isActivityToggled = pathname.includes('/activity')
const setMarketCount = useCollectionFilters((state) => state.setMarketCount) const setMarketCount = useCollectionFilters((state) => state.setMarketCount)
const isBagExpanded = useBag((state) => state.bagExpanded) const isBagExpanded = useBag((state) => state.bagExpanded)
const { chainId } = useWeb3React()
const { data: collectionStats, isLoading } = useQuery(['collectionStats', contractAddress], () => const { data: collectionStats, isLoading } = useQuery(['collectionStats', contractAddress], () =>
CollectionStatsFetcher(contractAddress as string) CollectionStatsFetcher(contractAddress as string)
@ -61,6 +65,11 @@ const Collection = () => {
return ( return (
<> <>
<Trace
page={PageName.NFT_COLLECTION_PAGE}
properties={{ collection_address: contractAddress, chain_id: chainId }}
shouldLogImpression
>
<Column width="full"> <Column width="full">
{contractAddress ? ( {contractAddress ? (
<> <>
@ -130,6 +139,7 @@ const Collection = () => {
!isLoading && <div className={styles.noCollectionAssets}>No collection assets exist at this address</div> !isLoading && <div className={styles.noCollectionAssets}>No collection assets exist at this address</div>
)} )}
</Column> </Column>
</Trace>
<MobileHoverBag /> <MobileHoverBag />
</> </>
) )