diff --git a/packages/common/src/models/Analytics.ts b/packages/common/src/models/Analytics.ts index d2fbb4e0c8a..65dddbbddd8 100644 --- a/packages/common/src/models/Analytics.ts +++ b/packages/common/src/models/Analytics.ts @@ -1172,6 +1172,9 @@ type PlaybackPlay = { isPreview?: boolean source: PlaybackSource collectionId?: string + // Which feed view the play originated from (matches FEED_CHANGE_VIEW's + // `view` values). Only present for plays coming from the feed lineup. + feed_type?: FeedTab } type PlaybackPause = { eventName: Name.PLAYBACK_PAUSE diff --git a/packages/common/src/store/playback/selectors.ts b/packages/common/src/store/playback/selectors.ts index d25706131fa..092f80eede6 100644 --- a/packages/common/src/store/playback/selectors.ts +++ b/packages/common/src/store/playback/selectors.ts @@ -29,6 +29,9 @@ export const getCurrentSource = (state: CommonState) => export const getCollectionId = (state: CommonState) => getCurrentPlaybackTrack(state)?.collectionId ?? null +export const getFeedType = (state: CommonState) => + getCurrentPlaybackTrack(state)?.feedType ?? null + export const getCurrentPlayerBehavior = (state: CommonState) => getCurrentPlaybackTrack(state)?.playerBehavior ?? PlayerBehavior.FULL_OR_PREVIEW diff --git a/packages/common/src/store/playback/types.ts b/packages/common/src/store/playback/types.ts index e782260f893..ab9a10da0de 100644 --- a/packages/common/src/store/playback/types.ts +++ b/packages/common/src/store/playback/types.ts @@ -1,4 +1,4 @@ -import { ID, Track, User } from '../../models' +import { FeedTab, ID, Track, User } from '../../models' export const PLAYBACK_RATE_LS_KEY = 'playbackRate' @@ -92,6 +92,11 @@ export type PlaybackTrack = { // Set by collection-page when building its queue; consumed by analytics // (PLAYBACK_PLAY events). collectionId?: ID + // The feed view (FOR_YOU / LATEST) this entry was queued from, if any. + // Set by the feed lineup when building its queue; consumed by analytics + // (PLAYBACK_PLAY events). Stamped at queue time so passive plays report + // the view the track actually came from even if the user switches tabs. + feedType?: FeedTab playerBehavior?: PlayerBehavior } diff --git a/packages/web/src/common/store/playback/sagas.ts b/packages/web/src/common/store/playback/sagas.ts index 164181d547f..331ae4ecf9c 100644 --- a/packages/web/src/common/store/playback/sagas.ts +++ b/packages/web/src/common/store/playback/sagas.ts @@ -69,6 +69,7 @@ const { getCurrentPlayerBehavior, getCurrentTrackId, getCollectionId, + getFeedType, getOvershot, getPlaybackIndex, getPlaybackQueue, @@ -772,11 +773,13 @@ function* watchNext() { } else { yield* call(playCurrent) const collId = yield* select(getCollectionId) + const feedType = yield* select(getFeedType) yield* put( make(Name.PLAYBACK_PLAY, { id: `${trackId}`, source: AnalyticsPlaybackSource.PASSIVE, - ...(collId ? { collectionId: collId } : {}) + ...(collId ? { collectionId: collId } : {}), + ...(feedType ? { feed_type: feedType } : {}) }) ) } @@ -811,11 +814,13 @@ function* watchPrevious() { if (track) { yield* call(playCurrent) const collId = yield* select(getCollectionId) + const feedType = yield* select(getFeedType) yield* put( make(Name.PLAYBACK_PLAY, { id: `${trackId}`, source: AnalyticsPlaybackSource.PASSIVE, - ...(collId ? { collectionId: collId } : {}) + ...(collId ? { collectionId: collId } : {}), + ...(feedType ? { feed_type: feedType } : {}) }) ) } else { diff --git a/packages/web/src/components/lineup/TrackLineup.tsx b/packages/web/src/components/lineup/TrackLineup.tsx index 86618f0da4a..a99ef5b9fbf 100644 --- a/packages/web/src/components/lineup/TrackLineup.tsx +++ b/packages/web/src/components/lineup/TrackLineup.tsx @@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import type { LineupData } from '@audius/common/api' import { usePlayTrack, usePauseTrack } from '@audius/common/hooks' -import { ID, PlaybackSource, Name } from '@audius/common/models' +import { FeedTab, ID, PlaybackSource, Name } from '@audius/common/models' import { playbackActions, playbackSelectors } from '@audius/common/store' import type { PlaybackTrack, @@ -96,6 +96,11 @@ export type TrackLineupProps = { onClickTile?: (trackId: ID) => void playbackSource?: PlaybackSource + + // The feed view (FOR_YOU / LATEST) this lineup renders, when it's the + // feed. Stamped onto queue entries and attached to PLAYBACK_PLAY events + // as `feed_type` so plays can be attributed to a feed view. + feedType?: FeedTab } /** @@ -134,7 +139,8 @@ export const TrackLineup = ({ delineatorMap, elementAdornment, onClickTile, - playbackSource = PlaybackSource.TRACK_TILE_LINEUP + playbackSource = PlaybackSource.TRACK_TILE_LINEUP, + feedType }: TrackLineupProps) => { const dispatch = useDispatch() const isMobile = useIsMobile() @@ -181,9 +187,10 @@ export const TrackLineup = ({ () => playbackTrackIds.map((id) => ({ trackId: id, - source + source, + ...(feedType ? { feedType } : {}) })), - [playbackTrackIds, source] + [playbackTrackIds, source, feedType] ) const togglePlay = useCallback( @@ -204,7 +211,11 @@ export const TrackLineup = ({ if (isSameTile && !isPlaying) { dispatch(playbackActions.play()) dispatch( - make(Name.PLAYBACK_PLAY, { id: `${trackId}`, source: analytics }) + make(Name.PLAYBACK_PLAY, { + id: `${trackId}`, + source: analytics, + ...(feedType ? { feed_type: feedType } : {}) + }) ) return } @@ -218,7 +229,11 @@ export const TrackLineup = ({ }) ) dispatch( - make(Name.PLAYBACK_PLAY, { id: `${trackId}`, source: analytics }) + make(Name.PLAYBACK_PLAY, { + id: `${trackId}`, + source: analytics, + ...(feedType ? { feed_type: feedType } : {}) + }) ) }, [ @@ -230,7 +245,8 @@ export const TrackLineup = ({ currentLegacy?.source, source, isPlaying, - playbackSource + playbackSource, + feedType ] ) diff --git a/packages/web/src/pages/feed-page/components/desktop/FeedPageContent.tsx b/packages/web/src/pages/feed-page/components/desktop/FeedPageContent.tsx index a654be8936e..4fd92aebc3a 100644 --- a/packages/web/src/pages/feed-page/components/desktop/FeedPageContent.tsx +++ b/packages/web/src/pages/feed-page/components/desktop/FeedPageContent.tsx @@ -159,6 +159,7 @@ const FeedPageContent = ({ containerRef }: FeedPageContentProps) => { key={`feed-${feedTab}`} aria-label='feed' source='DISCOVER_FEED' + feedType={feedTab} variant={LineupVariant.MAIN} scrollParent={containerRef?.current ?? null} emptyElement={} diff --git a/packages/web/src/pages/feed-page/components/mobile/FeedPageContent.tsx b/packages/web/src/pages/feed-page/components/mobile/FeedPageContent.tsx index 68d64c862e4..a1c0da371f8 100644 --- a/packages/web/src/pages/feed-page/components/mobile/FeedPageContent.tsx +++ b/packages/web/src/pages/feed-page/components/mobile/FeedPageContent.tsx @@ -162,6 +162,7 @@ const FeedPageMobileContent = ({ key={`feed-${feedTab}`} aria-label='feed' source='DISCOVER_FEED' + feedType={feedTab} ordered variant={LineupVariant.MAIN} scrollParent={containerRef?.current ?? null}