Commit 74accb2b authored by Greg Bugyis's avatar Greg Bugyis Committed by GitHub

feat: Search result events (#5025)

parent 28911983
...@@ -12,6 +12,7 @@ export enum EventName { ...@@ -12,6 +12,7 @@ export enum EventName {
EXPLORE_SEARCH_SELECTED = 'Explore Search Selected', EXPLORE_SEARCH_SELECTED = 'Explore Search Selected',
EXPLORE_TOKEN_ROW_CLICKED = 'Explore Token Row Clicked', EXPLORE_TOKEN_ROW_CLICKED = 'Explore Token Row Clicked',
PAGE_VIEWED = 'Page Viewed', PAGE_VIEWED = 'Page Viewed',
NAVBAR_RESULT_SELECTED = 'Navbar Result Selected',
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_ACTIVITY_SELECTED = 'NFT Activity Selected',
...@@ -97,6 +98,7 @@ export enum PageName { ...@@ -97,6 +98,7 @@ export enum PageName {
export enum SectionName { export enum SectionName {
CURRENCY_INPUT_PANEL = 'swap-currency-input', CURRENCY_INPUT_PANEL = 'swap-currency-input',
CURRENCY_OUTPUT_PANEL = 'swap-currency-output', CURRENCY_OUTPUT_PANEL = 'swap-currency-output',
NAVBAR_SEARCH = 'Navbar Search',
WIDGET = 'widget', WIDGET = 'widget',
// alphabetize additional section names. // alphabetize additional section names.
} }
...@@ -148,6 +150,15 @@ export enum Event { ...@@ -148,6 +150,15 @@ export enum Event {
// alphabetize additional events. // alphabetize additional events.
} }
/** Known navbar search result types */
export enum NavBarSearchTypes {
COLLECTION_SUGGESTION = 'collection-suggestion',
COLLECTION_TRENDING = 'collection-trending',
RECENT_SEARCH = 'recent',
TOKEN_SUGGESTION = 'token-suggestion',
TOKEN_TRENDING = 'token-trending',
}
/** /**
* Known Filter Types for NFTs * Known Filter Types for NFTs
*/ */
......
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import { t } from '@lingui/macro' import { t } from '@lingui/macro'
import { sendAnalyticsEvent } from 'analytics' import { sendAnalyticsEvent } from 'analytics'
import { ElementName, Event, EventName } from 'analytics/constants' import { ElementName, Event, EventName, SectionName } from 'analytics/constants'
import { Trace } from 'analytics/Trace'
import { useTrace } from 'analytics/Trace'
import { TraceEvent } from 'analytics/TraceEvent' import { TraceEvent } from 'analytics/TraceEvent'
import clsx from 'clsx' import clsx from 'clsx'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft' import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
...@@ -95,77 +97,85 @@ export const SearchBar = () => { ...@@ -95,77 +97,85 @@ export const SearchBar = () => {
const showCenteredSearchContent = const showCenteredSearchContent =
!isOpen && phase1Flag !== NftVariant.Enabled && !isMobileOrTablet && searchValue.length === 0 !isOpen && phase1Flag !== NftVariant.Enabled && !isMobileOrTablet && searchValue.length === 0
const trace = useTrace({ section: SectionName.NAVBAR_SEARCH })
const navbarSearchEventProperties = { const navbarSearchEventProperties = {
navbar_search_input_text: debouncedSearchValue, navbar_search_input_text: debouncedSearchValue,
hasInput: debouncedSearchValue && debouncedSearchValue.length > 0,
...trace,
} }
return ( return (
<Box position="relative"> <Box position="relative">
<Box <Trace section={SectionName.NAVBAR_SEARCH}>
position={{ sm: 'fixed', md: 'absolute' }} <Box
width={{ sm: isOpen ? 'viewWidth' : 'auto', md: 'auto' }} position={{ sm: 'fixed', md: 'absolute' }}
ref={searchRef} width={{ sm: isOpen ? 'viewWidth' : 'auto', md: 'auto' }}
className={styles.searchBarContainer} ref={searchRef}
display={{ sm: isOpen ? 'inline-block' : 'none', xl: 'inline-block' }} className={styles.searchBarContainer}
> display={{ sm: isOpen ? 'inline-block' : 'none', xl: 'inline-block' }}
<Row
className={clsx(
` ${styles.searchBar} ${!isOpen && !isMobile && magicalGradientOnHover} ${
isMobileOrTablet && (isOpen ? styles.visible : styles.hidden)
}`
)}
borderRadius={isOpen || isMobileOrTablet ? undefined : '12'}
borderTopRightRadius={isOpen && !isMobile ? '12' : undefined}
borderTopLeftRadius={isOpen && !isMobile ? '12' : undefined}
borderBottomWidth={isOpen || isMobileOrTablet ? '0px' : '1px'}
onClick={() => !isOpen && toggleOpen()}
gap="12"
> >
<Box className={showCenteredSearchContent ? styles.searchContentCentered : styles.searchContentLeftAlign}> <Row
<Box display={{ sm: 'none', md: 'flex' }}> className={clsx(
<MagnifyingGlassIcon /> ` ${styles.searchBar} ${!isOpen && !isMobile && magicalGradientOnHover} ${
</Box> isMobileOrTablet && (isOpen ? styles.visible : styles.hidden)
<Box display={{ sm: 'flex', md: 'none' }} color="textTertiary" onClick={toggleOpen}> }`
<ChevronLeftIcon /> )}
borderRadius={isOpen || isMobileOrTablet ? undefined : '12'}
borderTopRightRadius={isOpen && !isMobile ? '12' : undefined}
borderTopLeftRadius={isOpen && !isMobile ? '12' : undefined}
borderBottomWidth={isOpen || isMobileOrTablet ? '0px' : '1px'}
onClick={() => !isOpen && toggleOpen()}
gap="12"
>
<Box className={showCenteredSearchContent ? styles.searchContentCentered : styles.searchContentLeftAlign}>
<Box display={{ sm: 'none', md: 'flex' }}>
<MagnifyingGlassIcon />
</Box>
<Box display={{ sm: 'flex', md: 'none' }} color="textTertiary" onClick={toggleOpen}>
<ChevronLeftIcon />
</Box>
</Box> </Box>
<TraceEvent
events={[Event.onFocus]}
name={EventName.NAVBAR_SEARCH_SELECTED}
element={ElementName.NAVBAR_SEARCH_INPUT}
properties={{ ...trace }}
>
<Box
as="input"
placeholder={placeholderText}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
!isOpen && toggleOpen()
setSearchValue(event.target.value)
}}
onBlur={() => sendAnalyticsEvent(EventName.NAVBAR_SEARCH_EXITED, navbarSearchEventProperties)}
className={`${styles.searchBarInput} ${
showCenteredSearchContent ? styles.searchContentCentered : styles.searchContentLeftAlign
}`}
value={searchValue}
ref={inputRef}
width={phase1Flag === NftVariant.Enabled || isOpen ? 'full' : '160'}
/>
</TraceEvent>
</Row>
<Box className={clsx(isOpen ? styles.visible : styles.hidden)}>
{isOpen && (
<SearchBarDropdown
toggleOpen={toggleOpen}
tokens={reducedTokens}
collections={reducedCollections}
queryText={debouncedSearchValue}
hasInput={debouncedSearchValue.length > 0}
isLoading={tokensAreLoading || (collectionsAreLoading && phase1Flag === NftVariant.Enabled)}
/>
)}
</Box> </Box>
<TraceEvent
events={[Event.onFocus]}
name={EventName.NAVBAR_SEARCH_SELECTED}
element={ElementName.NAVBAR_SEARCH_INPUT}
>
<Box
as="input"
placeholder={placeholderText}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
!isOpen && toggleOpen()
setSearchValue(event.target.value)
}}
onBlur={() => sendAnalyticsEvent(EventName.NAVBAR_SEARCH_EXITED, navbarSearchEventProperties)}
className={`${styles.searchBarInput} ${
showCenteredSearchContent ? styles.searchContentCentered : styles.searchContentLeftAlign
}`}
value={searchValue}
ref={inputRef}
width={phase1Flag === NftVariant.Enabled || isOpen ? 'full' : '160'}
/>
</TraceEvent>
</Row>
<Box className={clsx(isOpen ? styles.visible : styles.hidden)}>
{isOpen && (
<SearchBarDropdown
toggleOpen={toggleOpen}
tokens={reducedTokens}
collections={reducedCollections}
hasInput={debouncedSearchValue.length > 0}
isLoading={tokensAreLoading || (collectionsAreLoading && phase1Flag === NftVariant.Enabled)}
/>
)}
</Box> </Box>
</Box> <NavIcon onClick={toggleOpen}>
<NavIcon onClick={toggleOpen}> <NavMagnifyingGlassIcon />
<NavMagnifyingGlassIcon /> </NavIcon>
</NavIcon> </Trace>
</Box> </Box>
) )
} }
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { sendAnalyticsEvent } from 'analytics' import { NavBarSearchTypes, SectionName } from 'analytics/constants'
import { EventName } from 'analytics/constants' import { useTrace } from 'analytics/Trace'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft' import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
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'
...@@ -31,6 +31,7 @@ interface SearchBarDropdownSectionProps { ...@@ -31,6 +31,7 @@ interface SearchBarDropdownSectionProps {
startingIndex: number startingIndex: number
setHoveredIndex: (index: number | undefined) => void setHoveredIndex: (index: number | undefined) => void
isLoading?: boolean isLoading?: boolean
eventProperties: Record<string, unknown>
} }
export const SearchBarDropdownSection = ({ export const SearchBarDropdownSection = ({
...@@ -42,6 +43,7 @@ export const SearchBarDropdownSection = ({ ...@@ -42,6 +43,7 @@ export const SearchBarDropdownSection = ({
startingIndex, startingIndex,
setHoveredIndex, setHoveredIndex,
isLoading, isLoading,
eventProperties,
}: SearchBarDropdownSectionProps) => { }: SearchBarDropdownSectionProps) => {
return ( return (
<Column gap="12"> <Column gap="12">
...@@ -60,16 +62,13 @@ export const SearchBarDropdownSection = ({ ...@@ -60,16 +62,13 @@ export const SearchBarDropdownSection = ({
isHovered={hoveredIndex === index + startingIndex} isHovered={hoveredIndex === index + startingIndex}
setHoveredIndex={setHoveredIndex} setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen} toggleOpen={toggleOpen}
traceEvent={() =>
sendAnalyticsEvent(EventName.NAVBAR_SEARCH_EXITED, {
position: index,
selected_type: 'collection',
suggestion_count: suggestions.length,
selected_name: suggestion.name,
selected_address: suggestion.address,
})
}
index={index + startingIndex} index={index + startingIndex}
eventProperties={{
position: index + startingIndex,
selected_search_result_name: suggestion.name,
selected_search_result_address: suggestion.address,
...eventProperties,
}}
/> />
) : ( ) : (
<TokenRow <TokenRow
...@@ -78,16 +77,13 @@ export const SearchBarDropdownSection = ({ ...@@ -78,16 +77,13 @@ export const SearchBarDropdownSection = ({
isHovered={hoveredIndex === index + startingIndex} isHovered={hoveredIndex === index + startingIndex}
setHoveredIndex={setHoveredIndex} setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen} toggleOpen={toggleOpen}
traceEvent={() =>
sendAnalyticsEvent(EventName.NAVBAR_SEARCH_EXITED, {
position: index,
selected_type: 'token',
suggestion_count: suggestions.length,
selected_name: suggestion.name,
selected_address: suggestion.address,
})
}
index={index + startingIndex} index={index + startingIndex}
eventProperties={{
position: index + startingIndex,
selected_search_result_name: suggestion.name,
selected_search_result_address: suggestion.address,
...eventProperties,
}}
/> />
) )
)} )}
...@@ -100,11 +96,19 @@ interface SearchBarDropdownProps { ...@@ -100,11 +96,19 @@ interface SearchBarDropdownProps {
toggleOpen: () => void toggleOpen: () => void
tokens: FungibleToken[] tokens: FungibleToken[]
collections: GenieCollection[] collections: GenieCollection[]
queryText: string
hasInput: boolean hasInput: boolean
isLoading: boolean isLoading: boolean
} }
export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, isLoading }: SearchBarDropdownProps) => { export const SearchBarDropdown = ({
toggleOpen,
tokens,
collections,
queryText,
hasInput,
isLoading,
}: SearchBarDropdownProps) => {
const [hoveredIndex, setHoveredIndex] = useState<number | undefined>(0) const [hoveredIndex, setHoveredIndex] = useState<number | undefined>(0)
const { history: searchHistory, updateItem: updateSearchHistory } = useSearchHistory() const { history: searchHistory, updateItem: updateSearchHistory } = useSearchHistory()
const shortenedHistory = useMemo(() => searchHistory.slice(0, 2), [searchHistory]) const shortenedHistory = useMemo(() => searchHistory.slice(0, 2), [searchHistory])
...@@ -192,7 +196,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i ...@@ -192,7 +196,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i
} }
}, [toggleOpen, hoveredIndex, totalSuggestions]) }, [toggleOpen, hoveredIndex, totalSuggestions])
const trace = useTrace({ section: SectionName.NAVBAR_SEARCH })
useEffect(() => { useEffect(() => {
const eventProperties = { total_suggestions: totalSuggestions, query_text: queryText, ...trace }
if (!isLoading) { if (!isLoading) {
const tokenSearchResults = const tokenSearchResults =
tokens.length > 0 ? ( tokens.length > 0 ? (
...@@ -202,6 +209,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i ...@@ -202,6 +209,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i
setHoveredIndex={setHoveredIndex} setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen} toggleOpen={toggleOpen}
suggestions={tokens} suggestions={tokens}
eventProperties={{
suggestion_type: NavBarSearchTypes.TOKEN_SUGGESTION,
...eventProperties,
}}
header={<Trans>Tokens</Trans>} header={<Trans>Tokens</Trans>}
/> />
) : ( ) : (
...@@ -219,6 +230,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i ...@@ -219,6 +230,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i
setHoveredIndex={setHoveredIndex} setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen} toggleOpen={toggleOpen}
suggestions={collections} suggestions={collections}
eventProperties={{
suggestion_type: NavBarSearchTypes.COLLECTION_SUGGESTION,
...eventProperties,
}}
header={<Trans>NFT Collections</Trans>} header={<Trans>NFT Collections</Trans>}
/> />
) : ( ) : (
...@@ -252,6 +267,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i ...@@ -252,6 +267,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i
setHoveredIndex={setHoveredIndex} setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen} toggleOpen={toggleOpen}
suggestions={shortenedHistory} suggestions={shortenedHistory}
eventProperties={{
suggestion_type: NavBarSearchTypes.RECENT_SEARCH,
...eventProperties,
}}
header={<Trans>Recent searches</Trans>} header={<Trans>Recent searches</Trans>}
headerIcon={<ClockIcon />} headerIcon={<ClockIcon />}
/> />
...@@ -263,6 +282,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i ...@@ -263,6 +282,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i
setHoveredIndex={setHoveredIndex} setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen} toggleOpen={toggleOpen}
suggestions={trendingTokens} suggestions={trendingTokens}
eventProperties={{
suggestion_type: NavBarSearchTypes.TOKEN_TRENDING,
...eventProperties,
}}
header={<Trans>Popular tokens</Trans>} header={<Trans>Popular tokens</Trans>}
headerIcon={<TrendingArrow />} headerIcon={<TrendingArrow />}
isLoading={trendingTokensAreLoading} isLoading={trendingTokensAreLoading}
...@@ -275,6 +298,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i ...@@ -275,6 +298,10 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i
setHoveredIndex={setHoveredIndex} setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen} toggleOpen={toggleOpen}
suggestions={trendingCollections as unknown as GenieCollection[]} suggestions={trendingCollections as unknown as GenieCollection[]}
eventProperties={{
suggestion_type: NavBarSearchTypes.COLLECTION_TRENDING,
...eventProperties,
}}
header={<Trans>Popular NFT collections</Trans>} header={<Trans>Popular NFT collections</Trans>}
headerIcon={<TrendingArrow />} headerIcon={<TrendingArrow />}
isLoading={trendingCollectionsAreLoading} isLoading={trendingCollectionsAreLoading}
...@@ -300,6 +327,9 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i ...@@ -300,6 +327,9 @@ export const SearchBarDropdown = ({ toggleOpen, tokens, collections, hasInput, i
hasInput, hasInput,
isNFTPage, isNFTPage,
isTokenPage, isTokenPage,
queryText,
totalSuggestions,
trace,
]) ])
return ( return (
......
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { sendAnalyticsEvent } from 'analytics'
import { EventName } from 'analytics/constants'
import clsx from 'clsx' import clsx from 'clsx'
import { L2NetworkLogo, LogoContainer } from 'components/Tokens/TokenTable/TokenRow' import { L2NetworkLogo, LogoContainer } from 'components/Tokens/TokenTable/TokenRow'
import TokenSafetyIcon from 'components/TokenSafety/TokenSafetyIcon' import TokenSafetyIcon from 'components/TokenSafety/TokenSafetyIcon'
...@@ -25,8 +27,8 @@ interface CollectionRowProps { ...@@ -25,8 +27,8 @@ interface CollectionRowProps {
isHovered: boolean isHovered: boolean
setHoveredIndex: (index: number | undefined) => void setHoveredIndex: (index: number | undefined) => void
toggleOpen: () => void toggleOpen: () => void
traceEvent: () => void
index: number index: number
eventProperties: Record<string, unknown>
} }
export const CollectionRow = ({ export const CollectionRow = ({
...@@ -34,8 +36,8 @@ export const CollectionRow = ({ ...@@ -34,8 +36,8 @@ export const CollectionRow = ({
isHovered, isHovered,
setHoveredIndex, setHoveredIndex,
toggleOpen, toggleOpen,
traceEvent,
index, index,
eventProperties,
}: CollectionRowProps) => { }: CollectionRowProps) => {
const [brokenImage, setBrokenImage] = useState(false) const [brokenImage, setBrokenImage] = useState(false)
const [loaded, setLoaded] = useState(false) const [loaded, setLoaded] = useState(false)
...@@ -47,8 +49,8 @@ export const CollectionRow = ({ ...@@ -47,8 +49,8 @@ export const CollectionRow = ({
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
addToSearchHistory(collection) addToSearchHistory(collection)
toggleOpen() toggleOpen()
traceEvent() sendAnalyticsEvent(EventName.NAVBAR_RESULT_SELECTED, { ...eventProperties })
}, [addToSearchHistory, collection, toggleOpen, traceEvent]) }, [addToSearchHistory, collection, toggleOpen, eventProperties])
useEffect(() => { useEffect(() => {
const keyDownHandler = (event: KeyboardEvent) => { const keyDownHandler = (event: KeyboardEvent) => {
...@@ -120,11 +122,11 @@ interface TokenRowProps { ...@@ -120,11 +122,11 @@ interface TokenRowProps {
isHovered: boolean isHovered: boolean
setHoveredIndex: (index: number | undefined) => void setHoveredIndex: (index: number | undefined) => void
toggleOpen: () => void toggleOpen: () => void
traceEvent: () => void
index: number index: number
eventProperties: Record<string, unknown>
} }
export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, traceEvent, index }: TokenRowProps) => { export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index, eventProperties }: TokenRowProps) => {
const [brokenImage, setBrokenImage] = useState(false) const [brokenImage, setBrokenImage] = useState(false)
const [loaded, setLoaded] = useState(false) const [loaded, setLoaded] = useState(false)
const addToSearchHistory = useSearchHistory( const addToSearchHistory = useSearchHistory(
...@@ -135,8 +137,8 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, traceE ...@@ -135,8 +137,8 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, traceE
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
addToSearchHistory(token) addToSearchHistory(token)
toggleOpen() toggleOpen()
traceEvent() sendAnalyticsEvent(EventName.NAVBAR_RESULT_SELECTED, { ...eventProperties })
}, [addToSearchHistory, toggleOpen, token, traceEvent]) }, [addToSearchHistory, toggleOpen, token, eventProperties])
const [bridgedAddress, bridgedChain, L2Icon] = useBridgedAddress(token) const [bridgedAddress, bridgedChain, L2Icon] = useBridgedAddress(token)
const tokenDetailsPath = getTokenDetailsURL(bridgedAddress ?? token.address, undefined, bridgedChain ?? token.chainId) const tokenDetailsPath = getTokenDetailsURL(bridgedAddress ?? token.address, undefined, bridgedChain ?? token.chainId)
......
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