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 {
NFT_BUY_BAG_SUCCEEDED = 'NFT Buy Bag Succeeded',
NFT_FILTER_OPENED = 'NFT Collection Filter Opened',
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',
SWAP_AUTOROUTER_VISUALIZATION_EXPANDED = 'Swap Autorouter Visualization Expanded',
SWAP_DETAILS_EXPANDED = 'Swap Details Expanded',
......@@ -88,6 +93,7 @@ export enum PageName {
NFT_COLLECTION_PAGE = 'nft-collection-page',
NFT_DETAILS_PAGE = 'nft-details-page',
NFT_EXPLORE_PAGE = 'nft-explore-page',
NFT_PROFILE_PAGE = 'nft-profile-page',
TOKEN_DETAILS_PAGE = 'token-details',
TOKENS_PAGE = 'tokens-page',
POOL_PAGE = 'pool-page',
......@@ -112,6 +118,7 @@ export enum SectionName {
/** Known modals for analytics purposes. */
export enum ModalName {
CONFIRM_SWAP = 'confirm-swap-modal',
NFT_LISTING = 'nft-listing-modal',
NFT_TX_COMPLETE = 'nft-tx-complete-modal',
TOKEN_SELECTOR = 'token-selector-modal',
// alphabetize additional modal names.
......
import { addressesByNetwork, SupportedChainId } from '@looksrare/sdk'
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 { Column, Row } from 'nft/components/Flex'
import { ChevronLeftIcon, XMarkIcon } from 'nft/components/icons'
......@@ -8,6 +12,7 @@ import { themeVars } from 'nft/css/sprinkles.css'
import { useBag, useIsMobile, useNFTList, useSellAsset } from 'nft/hooks'
import { logListing, looksRareNonceFetcher } from 'nft/queries'
import { AssetRow, CollectionRow, ListingRow, ListingStatus } from 'nft/types'
import { fetchPrice } from 'nft/utils/fetchPrice'
import { pluralize } from 'nft/utils/roundAndPluralize'
import { Dispatch, useEffect, useMemo, useRef, useState } from 'react'
......@@ -34,6 +39,7 @@ const ListingModal = () => {
const toggleCart = useBag((state) => state.toggleBag)
const looksRareNonceRef = useRef(looksRareNonce)
const isMobile = useIsMobile()
const trace = useTrace({ modal: ModalName.NFT_LISTING })
useEffect(() => {
useNFTList.subscribe((state) => (looksRareNonceRef.current = state.looksRareNonce))
......@@ -41,6 +47,29 @@ const ListingModal = () => {
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
useEffect(() => {
collectionsRequiringApproval?.length &&
......@@ -60,6 +89,7 @@ const ListingModal = () => {
const startListingFlow = async () => {
if (!signer) return
sendAnalyticsEvent(EventName.NFT_SELL_START_LISTING, { ...startListingEventProperties })
setListingStatus(ListingStatus.SIGNING)
const addresses = addressesByNetwork[SupportedChainId.MAINNET]
const signerAddress = await signer.getAddress()
......@@ -111,6 +141,11 @@ const ListingModal = () => {
} else if (!paused) {
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()) ?? '')
}
......@@ -144,6 +179,7 @@ const ListingModal = () => {
const showSuccessScreen = useMemo(() => listingStatus === ListingStatus.APPROVED, [listingStatus])
return (
<Trace modal={ModalName.NFT_LISTING}>
<Column paddingTop="20" paddingBottom="20" paddingLeft="12" paddingRight="12">
<Row className={headlineSmall} marginBottom="10">
{isMobile && !showSuccessScreen && (
......@@ -169,6 +205,10 @@ const ListingModal = () => {
</Row>
<Column overflowX="hidden" overflowY="auto" style={{ maxHeight: '60vh' }}>
{showSuccessScreen ? (
<Trace
name={EventName.NFT_LISTING_COMPLETED}
properties={{ list_quantity: listings.length, usd_value: ethPriceInUSD * totalEthListingValue, ...trace }}
>
<ListingSection
sectionTitle={`Listed ${listings.length} item${pluralize(listings.length)} for sale`}
rows={listings}
......@@ -176,6 +216,7 @@ const ListingModal = () => {
openIndex={openIndex}
isSuccessScreen={true}
/>
</Trace>
) : (
<>
<ListingSection
......@@ -231,6 +272,7 @@ const ListingModal = () => {
</Box>
)}
</Column>
</Trace>
)
}
......
import { Event, EventName } from 'analytics/constants'
import { TraceEvent } from 'analytics/TraceEvent'
import { useNftBalanceQuery } from 'graphql/data/nft/NftBalance'
import { AnimatedBox, Box } from 'nft/components/Box'
import { assetList } from 'nft/components/collection/CollectionNfts.css'
......@@ -137,10 +139,16 @@ export const ProfilePage = () => {
/>
<Row gap="8" flexWrap="nowrap">
{isSellMode && <SelectAllButton ownerAssets={ownerAssets ?? []} />}
<TraceEvent
events={[Event.onClick]}
name={EventName.NFT_SELL_SELECTED}
shouldLogImpression={!isSellMode}
>
<SellModeButton className={buttonTextMedium} active={isSellMode} onClick={handleSellModeClick}>
<TagIcon height={20} width={20} />
Sell
</SellModeButton>
</TraceEvent>
</Row>
</Row>
<Row>
......
import { sendAnalyticsEvent } from 'analytics'
import { EventName } from 'analytics/constants'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { VerifiedIcon } from 'nft/components/icons'
......@@ -33,6 +35,11 @@ export const WalletAssetDisplay = ({ asset, isSellMode }: { asset: WalletAsset;
const handleSelect = () => {
isSelected ? removeSellAsset(asset) : selectSellAsset(asset)
!isSelected &&
sendAnalyticsEvent(EventName.NFT_SELL_ITEM_ADDED, {
collection_address: asset.asset_contract.address,
token_id: asset.tokenId,
})
if (
!cartExpanded &&
!sellAssets.find(
......
import { useWeb3React } from '@web3-react/core'
import { PageName } from 'analytics/constants'
import { Trace } from 'analytics/Trace'
import { Box } from 'nft/components/Box'
import { Center, Column, Row } from 'nft/components/Flex'
import { ChevronLeftIcon, XMarkIcon } from 'nft/components/icons'
......@@ -45,6 +47,7 @@ const Profile = () => {
}
return (
<Trace page={PageName.NFT_PROFILE_PAGE} shouldLogImpression>
<Box className={styles.mobileSellWrapper}>
{/* <Head> TODO: figure out metadata tagging
<title>Genie | Sell</title>
......@@ -81,6 +84,7 @@ const Profile = () => {
</Column>
)}
</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