From 6995b118a7bea61af74e337d5201bd88f0edd208 Mon Sep 17 00:00:00 2001 From: LautaroPetaccio Date: Wed, 22 Jan 2025 16:48:16 -0300 Subject: [PATCH] feat: Add tracking to banner button --- package-lock.json | 8 ++--- package.json | 4 +-- src/containers/Banner/Banner.container.ts | 37 +++++++++++++++++++---- src/containers/Banner/Banner.types.ts | 8 +++-- src/lib/time.ts | 6 ++++ src/modules/campaign/actions.ts | 2 +- src/modules/campaign/sagas.spec.ts | 5 ++- src/modules/campaign/sagas.ts | 7 +++-- src/modules/campaign/selectors.spec.ts | 5 +-- src/modules/campaign/selectors.ts | 15 ++++++--- src/modules/campaign/types.ts | 2 +- 11 files changed, 72 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index f163d19e..d0d795a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "decentraland-crypto-fetch": "^2.0.1", "decentraland-transactions": "^2.18.0", "decentraland-ui": "^6.11.0", - "decentraland-ui2": "^0.9.2", + "decentraland-ui2": "^0.10.0", "ethers": "^5.6.8", "events": "^3.3.0", "flat": "^5.0.2", @@ -8801,9 +8801,9 @@ } }, "node_modules/decentraland-ui2": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/decentraland-ui2/-/decentraland-ui2-0.9.2.tgz", - "integrity": "sha512-5mCgR6wQ9KEcbb4SAMhNe5lx3DABH9sewKxD1Qy3+Ap3kMM7mH5iuDVHBvHnfBtHdD5iHNFzdbIUdGbprN1XJA==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/decentraland-ui2/-/decentraland-ui2-0.10.0.tgz", + "integrity": "sha512-feZXKmF76wNHkdokCLNicNLG0EMvRT5WgGCGDUJOYydWf9AIrezBkKTSJjmu/k6ZqyG54GTeenH6cuyzDwtQYQ==", "dependencies": { "@contentful/rich-text-react-renderer": "^16.0.1", "@dcl/schemas": "^15.6.0", diff --git a/package.json b/package.json index 7ec37986..aba5d524 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "decentraland-dapps", - "version": "0.0.0-development", + "version": "27.0.2", "type": "module", "main": "dist/index.js", "files": [ @@ -25,7 +25,7 @@ "decentraland-crypto-fetch": "^2.0.1", "decentraland-transactions": "^2.18.0", "decentraland-ui": "^6.11.0", - "decentraland-ui2": "^0.9.2", + "decentraland-ui2": "^0.10.0", "ethers": "^5.6.8", "events": "^3.3.0", "flat": "^5.0.2", diff --git a/src/containers/Banner/Banner.container.ts b/src/containers/Banner/Banner.container.ts index a8ae9ffe..bfdc5bed 100644 --- a/src/containers/Banner/Banner.container.ts +++ b/src/containers/Banner/Banner.container.ts @@ -6,13 +6,38 @@ import { getError, isLoading } from '../../modules/campaign' +import { getAnalytics } from '../../modules/analytics/utils' import { MapStateProps, OwnProps } from './Banner.types' +import { sleep } from '../../lib/time' -const mapState = (state: any, ownProps: OwnProps): MapStateProps => ({ - fields: getBanner(state, ownProps.id) ?? null, - assets: getBannerAssets(state, ownProps.id), - isLoading: isLoading(state), - error: getError(state) -}) +const mapState = (state: any, ownProps: OwnProps): MapStateProps => { + const fields = getBanner(state, ownProps.id) ?? null + const handleClick = (event: React.MouseEvent) => { + const anchorEvent = (event as unknown) as React.MouseEvent< + HTMLAnchorElement + > + anchorEvent.preventDefault() + + const analytics = getAnalytics() + if (analytics) { + analytics.track('CLICK_BANNER', { + id: fields?.id, + location: ownProps.id + }) + } + // Delay the navigation to allow the analytics to be tracked + sleep(300).then(() => { + window.location.href = (anchorEvent.target as HTMLAnchorElement).href + }) + } + + return { + onClick: handleClick, + fields, + assets: getBannerAssets(state, ownProps.id), + isLoading: isLoading(state), + error: getError(state) + } +} export default connect(mapState)(Banner) diff --git a/src/containers/Banner/Banner.types.ts b/src/containers/Banner/Banner.types.ts index 7d807df0..a7956522 100644 --- a/src/containers/Banner/Banner.types.ts +++ b/src/containers/Banner/Banner.types.ts @@ -1,6 +1,8 @@ -import { BannerProps as UIBannerProps } from "decentraland-ui2" +import { BannerProps as UIBannerProps } from 'decentraland-ui2' export type BannerProps = UIBannerProps & { id: string } export type OwnProps = Pick -export type MapStateProps = Pick - +export type MapStateProps = Pick< + BannerProps, + 'fields' | 'assets' | 'isLoading' | 'error' | 'onClick' +> diff --git a/src/lib/time.ts b/src/lib/time.ts index d0878eaa..8fe61310 100644 --- a/src/lib/time.ts +++ b/src/lib/time.ts @@ -1,3 +1,9 @@ export function fromMillisecondsToSeconds(timeInMilliseconds: number): number { return Math.floor(timeInMilliseconds / 1000) } + +export function sleep(delay: number) { + return new Promise(resolve => { + setTimeout(resolve, delay) + }) +} diff --git a/src/modules/campaign/actions.ts b/src/modules/campaign/actions.ts index b23fba3d..fc3c2461 100644 --- a/src/modules/campaign/actions.ts +++ b/src/modules/campaign/actions.ts @@ -8,7 +8,7 @@ export const FETCH_CAMPAIGN_FAILURE = '[Failure] Fetch Campaign' export const fetchCampaignRequest = () => action(FETCH_CAMPAIGN_REQUEST) export const fetchCampaignSuccess = ( - banners: Record, + banners: Record, assets: Record, name?: LocalizedField, tabName?: LocalizedField, diff --git a/src/modules/campaign/sagas.spec.ts b/src/modules/campaign/sagas.spec.ts index 00251253..f4a8c599 100644 --- a/src/modules/campaign/sagas.spec.ts +++ b/src/modules/campaign/sagas.spec.ts @@ -65,7 +65,10 @@ describe('when handling the fetch campaign request', () => { .put( fetchCampaignSuccess( { - marketplaceHomepageBanner: { ...mockHomepageBannerEntry.fields } + marketplaceHomepageBanner: { + ...mockHomepageBannerEntry.fields, + id: mockHomepageBannerEntry.sys.id + } }, { [marketplaceHomepageBannerAssets[0].sys.id]: diff --git a/src/modules/campaign/sagas.ts b/src/modules/campaign/sagas.ts index 4e90712d..e13ac96d 100644 --- a/src/modules/campaign/sagas.ts +++ b/src/modules/campaign/sagas.ts @@ -67,12 +67,15 @@ export function* campaignSagas( bannerEntry && bannerEntry.sys.contentType.sys.id === BANNER_CONTENT_TYPE ) { - acc[key] = (bannerEntry.fields as unknown) as BannerFields + acc[key] = { + ...bannerEntry.fields, + id: linkedEntryId + } as BannerFields & { id: string } } } return acc }, - {} as Record + {} as Record ) const campaignField = Object.values(items[0].fields).find(field => { diff --git a/src/modules/campaign/selectors.spec.ts b/src/modules/campaign/selectors.spec.ts index ecf1a68b..5fabf61d 100644 --- a/src/modules/campaign/selectors.spec.ts +++ b/src/modules/campaign/selectors.spec.ts @@ -26,7 +26,7 @@ type MockState = { let mockState: MockState let mockAsset: ContentfulAsset -let mockBanner: BannerFields +let mockBanner: BannerFields & { id: string } describe('Campaign selectors', () => { beforeEach(() => { @@ -68,6 +68,7 @@ describe('Campaign selectors', () => { } mockBanner = { + id: 'someId', fullSizeBackground: { 'en-US': { sys: { @@ -77,7 +78,7 @@ describe('Campaign selectors', () => { } } } - } as BannerFields + } as BannerFields & { id: string } mockState = { campaign: { diff --git a/src/modules/campaign/selectors.ts b/src/modules/campaign/selectors.ts index 71ac8124..a2efb04f 100644 --- a/src/modules/campaign/selectors.ts +++ b/src/modules/campaign/selectors.ts @@ -4,6 +4,8 @@ import { isLoadingType, type LoadingState } from '../loading' import { CampaignState } from './types' import { FETCH_CAMPAIGN_REQUEST } from './actions' +const isLocalizedField = (value: any): value is LocalizedField => typeof value === 'object' && value !== null && 'en-US' in value + export const getState = (state: any): CampaignState => state.campaign export const getData = (state: any): CampaignState['data'] | null => getState(state).data export const getLoading = (state: any): LoadingState => getState(state).loading @@ -19,7 +21,7 @@ export const getAllTags = (state: any): string[] => { } export const getAssets = (state: any): Record | null => getData(state)?.assets || null export const getTabName = (state: any): LocalizedField | null => getData(state)?.tabName || null -export const getBanner = (state: any, id: string): BannerFields | null => { +export const getBanner = (state: any, id: string): BannerFields & { id: string } | null => { return getData(state)?.banners[id] ?? null } export const getBannerAssets = (state: any, bannerId: string): Record => { @@ -29,10 +31,13 @@ export const getBannerAssets = (state: any, bannerId: string): Record { - if (isSysLink(value['en-US'])) { - const asset = assets?.[value['en-US'].sys.id] - if (asset) { - acc[value['en-US'].sys.id] = asset + if (isLocalizedField(value)) { + const usLocalizedValue = value['en-US'] + if (isSysLink(usLocalizedValue)) { + const asset = assets?.[usLocalizedValue.sys.id] + if (asset) { + acc[usLocalizedValue.sys.id] = asset + } } } return acc diff --git a/src/modules/campaign/types.ts b/src/modules/campaign/types.ts index 6bb32e4c..07750550 100644 --- a/src/modules/campaign/types.ts +++ b/src/modules/campaign/types.ts @@ -9,7 +9,7 @@ export type CampaignState = { tabName?: LocalizedField mainTag?: string additionalTags?: string[] - banners: Record + banners: Record assets: Record } | null loading: LoadingState