Commit 48d59551 authored by Greg Bugyis's avatar Greg Bugyis Committed by GitHub

feat: Log NFT Sell events (#5106)

* Log profile page view

* Log sell flow started

* Add Start Listing event

* Add constant for list modal + useTrace

* Log sell item added

* Log listing completed

* Fix usd_value property

* Move log to startListingFlow

* Use Set to remove duplicate marketplaces

* Move listing completed event
parent dbf5c63e
...@@ -24,6 +24,11 @@ export enum EventName { ...@@ -24,6 +24,11 @@ export enum EventName {
NFT_BUY_BAG_SUCCEEDED = 'NFT Buy Bag Succeeded', NFT_BUY_BAG_SUCCEEDED = 'NFT Buy Bag Succeeded',
NFT_FILTER_OPENED = 'NFT Collection Filter Opened', NFT_FILTER_OPENED = 'NFT Collection Filter Opened',
NFT_FILTER_SELECTED = 'NFT Filter Selected', NFT_FILTER_SELECTED = 'NFT Filter Selected',
NFT_LISTING_SIGNED = 'NFT Listing Signed',
NFT_LISTING_COMPLETED = 'NFT Listing Success',
NFT_SELL_ITEM_ADDED = 'NFT Sell Item Added',
NFT_SELL_SELECTED = 'NFT Sell Selected',
NFT_SELL_START_LISTING = 'NFT Sell Start Listing',
NFT_TRENDING_ROW_SELECTED = 'Trending Row Selected', NFT_TRENDING_ROW_SELECTED = 'Trending Row 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',
...@@ -88,6 +93,7 @@ export enum PageName { ...@@ -88,6 +93,7 @@ export enum PageName {
NFT_COLLECTION_PAGE = 'nft-collection-page', NFT_COLLECTION_PAGE = 'nft-collection-page',
NFT_DETAILS_PAGE = 'nft-details-page', NFT_DETAILS_PAGE = 'nft-details-page',
NFT_EXPLORE_PAGE = 'nft-explore-page', NFT_EXPLORE_PAGE = 'nft-explore-page',
NFT_PROFILE_PAGE = 'nft-profile-page',
TOKEN_DETAILS_PAGE = 'token-details', TOKEN_DETAILS_PAGE = 'token-details',
TOKENS_PAGE = 'tokens-page', TOKENS_PAGE = 'tokens-page',
POOL_PAGE = 'pool-page', POOL_PAGE = 'pool-page',
...@@ -112,6 +118,7 @@ export enum SectionName { ...@@ -112,6 +118,7 @@ export enum SectionName {
/** Known modals for analytics purposes. */ /** Known modals for analytics purposes. */
export enum ModalName { export enum ModalName {
CONFIRM_SWAP = 'confirm-swap-modal', CONFIRM_SWAP = 'confirm-swap-modal',
NFT_LISTING = 'nft-listing-modal',
NFT_TX_COMPLETE = 'nft-tx-complete-modal', NFT_TX_COMPLETE = 'nft-tx-complete-modal',
TOKEN_SELECTOR = 'token-selector-modal', TOKEN_SELECTOR = 'token-selector-modal',
// alphabetize additional modal names. // alphabetize additional modal names.
......
import { addressesByNetwork, SupportedChainId } from '@looksrare/sdk' import { addressesByNetwork, SupportedChainId } from '@looksrare/sdk'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent } from 'analytics'
import { EventName, ModalName } from 'analytics/constants'
import { Trace } from 'analytics/Trace'
import { useTrace } from 'analytics/Trace'
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'
import { ChevronLeftIcon, XMarkIcon } from 'nft/components/icons' import { ChevronLeftIcon, XMarkIcon } from 'nft/components/icons'
...@@ -8,6 +12,7 @@ import { themeVars } from 'nft/css/sprinkles.css' ...@@ -8,6 +12,7 @@ import { themeVars } from 'nft/css/sprinkles.css'
import { useBag, useIsMobile, useNFTList, useSellAsset } from 'nft/hooks' import { useBag, useIsMobile, useNFTList, useSellAsset } from 'nft/hooks'
import { logListing, looksRareNonceFetcher } from 'nft/queries' import { logListing, looksRareNonceFetcher } from 'nft/queries'
import { AssetRow, CollectionRow, ListingRow, ListingStatus } from 'nft/types' import { AssetRow, CollectionRow, ListingRow, ListingStatus } from 'nft/types'
import { fetchPrice } from 'nft/utils/fetchPrice'
import { pluralize } from 'nft/utils/roundAndPluralize' import { pluralize } from 'nft/utils/roundAndPluralize'
import { Dispatch, useEffect, useMemo, useRef, useState } from 'react' import { Dispatch, useEffect, useMemo, useRef, useState } from 'react'
...@@ -34,6 +39,7 @@ const ListingModal = () => { ...@@ -34,6 +39,7 @@ const ListingModal = () => {
const toggleCart = useBag((state) => state.toggleBag) const toggleCart = useBag((state) => state.toggleBag)
const looksRareNonceRef = useRef(looksRareNonce) const looksRareNonceRef = useRef(looksRareNonce)
const isMobile = useIsMobile() const isMobile = useIsMobile()
const trace = useTrace({ modal: ModalName.NFT_LISTING })
useEffect(() => { useEffect(() => {
useNFTList.subscribe((state) => (looksRareNonceRef.current = state.looksRareNonce)) useNFTList.subscribe((state) => (looksRareNonceRef.current = state.looksRareNonce))
...@@ -41,6 +47,29 @@ const ListingModal = () => { ...@@ -41,6 +47,29 @@ const ListingModal = () => {
const totalEthListingValue = useMemo(() => getTotalEthValue(sellAssets), [sellAssets]) const totalEthListingValue = useMemo(() => getTotalEthValue(sellAssets), [sellAssets])
const [ethPriceInUSD, setEthPriceInUSD] = useState(0)
useEffect(() => {
fetchPrice().then((price) => {
setEthPriceInUSD(price || 0)
})
}, [])
const startListingEventProperties = {
collection_addresses: sellAssets.map((asset) => asset.asset_contract.address),
token_ids: sellAssets.map((asset) => asset.tokenId),
marketplaces: Array.from(new Set(listings.map((asset) => asset.marketplace.name))),
list_quantity: listings.length,
usd_value: ethPriceInUSD * totalEthListingValue,
...trace,
}
const approvalEventProperties = {
list_quantity: listings.length,
usd_value: ethPriceInUSD * totalEthListingValue,
...trace,
}
// when all collections have been approved, auto start the signing process // when all collections have been approved, auto start the signing process
useEffect(() => { useEffect(() => {
collectionsRequiringApproval?.length && collectionsRequiringApproval?.length &&
...@@ -60,6 +89,7 @@ const ListingModal = () => { ...@@ -60,6 +89,7 @@ const ListingModal = () => {
const startListingFlow = async () => { const startListingFlow = async () => {
if (!signer) return if (!signer) return
sendAnalyticsEvent(EventName.NFT_SELL_START_LISTING, { ...startListingEventProperties })
setListingStatus(ListingStatus.SIGNING) setListingStatus(ListingStatus.SIGNING)
const addresses = addressesByNetwork[SupportedChainId.MAINNET] const addresses = addressesByNetwork[SupportedChainId.MAINNET]
const signerAddress = await signer.getAddress() const signerAddress = await signer.getAddress()
...@@ -111,6 +141,11 @@ const ListingModal = () => { ...@@ -111,6 +141,11 @@ const ListingModal = () => {
} else if (!paused) { } else if (!paused) {
setListingStatus(ListingStatus.FAILED) setListingStatus(ListingStatus.FAILED)
} }
sendAnalyticsEvent(EventName.NFT_LISTING_COMPLETED, {
signatures_requested: listings.length,
signatures_approved: listings.filter((asset) => asset.status === ListingStatus.APPROVED),
...approvalEventProperties,
})
await logListing(listings, (await signer?.getAddress()) ?? '') await logListing(listings, (await signer?.getAddress()) ?? '')
} }
...@@ -144,6 +179,7 @@ const ListingModal = () => { ...@@ -144,6 +179,7 @@ const ListingModal = () => {
const showSuccessScreen = useMemo(() => listingStatus === ListingStatus.APPROVED, [listingStatus]) const showSuccessScreen = useMemo(() => listingStatus === ListingStatus.APPROVED, [listingStatus])
return ( return (
<Trace modal={ModalName.NFT_LISTING}>
<Column paddingTop="20" paddingBottom="20" paddingLeft="12" paddingRight="12"> <Column paddingTop="20" paddingBottom="20" paddingLeft="12" paddingRight="12">
<Row className={headlineSmall} marginBottom="10"> <Row className={headlineSmall} marginBottom="10">
{isMobile && !showSuccessScreen && ( {isMobile && !showSuccessScreen && (
...@@ -169,6 +205,10 @@ const ListingModal = () => { ...@@ -169,6 +205,10 @@ const ListingModal = () => {
</Row> </Row>
<Column overflowX="hidden" overflowY="auto" style={{ maxHeight: '60vh' }}> <Column overflowX="hidden" overflowY="auto" style={{ maxHeight: '60vh' }}>
{showSuccessScreen ? ( {showSuccessScreen ? (
<Trace
name={EventName.NFT_LISTING_COMPLETED}
properties={{ list_quantity: listings.length, usd_value: ethPriceInUSD * totalEthListingValue, ...trace }}
>
<ListingSection <ListingSection
sectionTitle={`Listed ${listings.length} item${pluralize(listings.length)} for sale`} sectionTitle={`Listed ${listings.length} item${pluralize(listings.length)} for sale`}
rows={listings} rows={listings}
...@@ -176,6 +216,7 @@ const ListingModal = () => { ...@@ -176,6 +216,7 @@ const ListingModal = () => {
openIndex={openIndex} openIndex={openIndex}
isSuccessScreen={true} isSuccessScreen={true}
/> />
</Trace>
) : ( ) : (
<> <>
<ListingSection <ListingSection
...@@ -231,6 +272,7 @@ const ListingModal = () => { ...@@ -231,6 +272,7 @@ const ListingModal = () => {
</Box> </Box>
)} )}
</Column> </Column>
</Trace>
) )
} }
......
import { Event, EventName } from 'analytics/constants'
import { TraceEvent } from 'analytics/TraceEvent'
import { useNftBalanceQuery } from 'graphql/data/nft/NftBalance' import { useNftBalanceQuery } from 'graphql/data/nft/NftBalance'
import { AnimatedBox, Box } from 'nft/components/Box' import { AnimatedBox, Box } from 'nft/components/Box'
import { assetList } from 'nft/components/collection/CollectionNfts.css' import { assetList } from 'nft/components/collection/CollectionNfts.css'
...@@ -137,10 +139,16 @@ export const ProfilePage = () => { ...@@ -137,10 +139,16 @@ export const ProfilePage = () => {
/> />
<Row gap="8" flexWrap="nowrap"> <Row gap="8" flexWrap="nowrap">
{isSellMode && <SelectAllButton ownerAssets={ownerAssets ?? []} />} {isSellMode && <SelectAllButton ownerAssets={ownerAssets ?? []} />}
<TraceEvent
events={[Event.onClick]}
name={EventName.NFT_SELL_SELECTED}
shouldLogImpression={!isSellMode}
>
<SellModeButton className={buttonTextMedium} active={isSellMode} onClick={handleSellModeClick}> <SellModeButton className={buttonTextMedium} active={isSellMode} onClick={handleSellModeClick}>
<TagIcon height={20} width={20} /> <TagIcon height={20} width={20} />
Sell Sell
</SellModeButton> </SellModeButton>
</TraceEvent>
</Row> </Row>
</Row> </Row>
<Row> <Row>
......
import { sendAnalyticsEvent } from 'analytics'
import { EventName } from 'analytics/constants'
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'
import { VerifiedIcon } from 'nft/components/icons' import { VerifiedIcon } from 'nft/components/icons'
...@@ -33,6 +35,11 @@ export const WalletAssetDisplay = ({ asset, isSellMode }: { asset: WalletAsset; ...@@ -33,6 +35,11 @@ export const WalletAssetDisplay = ({ asset, isSellMode }: { asset: WalletAsset;
const handleSelect = () => { const handleSelect = () => {
isSelected ? removeSellAsset(asset) : selectSellAsset(asset) isSelected ? removeSellAsset(asset) : selectSellAsset(asset)
!isSelected &&
sendAnalyticsEvent(EventName.NFT_SELL_ITEM_ADDED, {
collection_address: asset.asset_contract.address,
token_id: asset.tokenId,
})
if ( if (
!cartExpanded && !cartExpanded &&
!sellAssets.find( !sellAssets.find(
......
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { PageName } from 'analytics/constants'
import { Trace } from 'analytics/Trace'
import { Box } from 'nft/components/Box' import { Box } from 'nft/components/Box'
import { Center, Column, Row } from 'nft/components/Flex' import { Center, Column, Row } from 'nft/components/Flex'
import { ChevronLeftIcon, XMarkIcon } from 'nft/components/icons' import { ChevronLeftIcon, XMarkIcon } from 'nft/components/icons'
...@@ -45,6 +47,7 @@ const Profile = () => { ...@@ -45,6 +47,7 @@ const Profile = () => {
} }
return ( return (
<Trace page={PageName.NFT_PROFILE_PAGE} shouldLogImpression>
<Box className={styles.mobileSellWrapper}> <Box className={styles.mobileSellWrapper}>
{/* <Head> TODO: figure out metadata tagging {/* <Head> TODO: figure out metadata tagging
<title>Genie | Sell</title> <title>Genie | Sell</title>
...@@ -81,6 +84,7 @@ const Profile = () => { ...@@ -81,6 +84,7 @@ const Profile = () => {
</Column> </Column>
)} )}
</Box> </Box>
</Trace>
) )
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment