Skip to content

Commit

Permalink
Bug 1820707 - Record impressions and clicks for organic tiles. r=nanj
Browse files Browse the repository at this point in the history
Currently, the Glean `newtab` ping and legacy scalars collect
impressions and clicks for sponsored (Pocket and Contile) tiles, but
not organic tiles from Places.

This commit adds:

* Support for recording organic impressions to the
  `TopSiteImpressionWrapper` component.
* Support for recording organic clicks to the `TopSiteLink`
  component.
* Instrumentation for recording organic impressions and clicks,
  separate from sponsored ones, to `TelemetryFeed`.

Differential Revision: https://phabricator.services.mozilla.com/D171830
  • Loading branch information
linabutler committed Mar 11, 2023
1 parent 7e6a2ed commit a82057d
Show file tree
Hide file tree
Showing 12 changed files with 403 additions and 124 deletions.
3 changes: 2 additions & 1 deletion browser/components/newtab/common/Actions.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,12 @@ for (const type of [
"TOP_SITES_CANCEL_EDIT",
"TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL",
"TOP_SITES_EDIT",
"TOP_SITES_IMPRESSION_STATS",
"TOP_SITES_INSERT",
"TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL",
"TOP_SITES_ORGANIC_IMPRESSION_STATS",
"TOP_SITES_PIN",
"TOP_SITES_PREFS_UPDATED",
"TOP_SITES_SPONSORED_IMPRESSION_STATS",
"TOP_SITES_UNPIN",
"TOP_SITES_UPDATED",
"TOTAL_BOOKMARKS_REQUEST",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class ImpressionStats extends React.PureComponent {
for (const card of cards) {
this.props.dispatch(
ac.OnlyToMain({
type: at.TOP_SITES_IMPRESSION_STATS,
type: at.TOP_SITES_SPONSORED_IMPRESSION_STATS,
data: {
type: "impression",
tile_id: card.id,
Expand Down
126 changes: 81 additions & 45 deletions browser/components/newtab/content-src/components/TopSites/TopSite.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,58 @@ export class TopSiteLink extends React.PureComponent {
};
}

let impressionStats = null;
if (link.type === SPOC_TYPE) {
// Record impressions for Pocket tiles.
impressionStats = (
<ImpressionStats
flightId={link.flightId}
rows={[
{
id: link.id,
pos: link.pos,
shim: link.shim && link.shim.impression,
advertiser: title.toLocaleLowerCase(),
},
]}
dispatch={this.props.dispatch}
source={TOP_SITES_SOURCE}
/>
);
} else if (isSponsored(link)) {
// Record impressions for non-Pocket sponsored tiles.
impressionStats = (
<TopSiteImpressionWrapper
actionType={at.TOP_SITES_SPONSORED_IMPRESSION_STATS}
tile={{
position: this.props.index + 1,
tile_id: link.sponsored_tile_id || -1,
reporting_url: link.sponsored_impression_url,
advertiser: title.toLocaleLowerCase(),
source: NEWTAB_SOURCE,
}}
// For testing.
IntersectionObserver={this.props.IntersectionObserver}
document={this.props.document}
dispatch={this.props.dispatch}
/>
);
} else {
// Record impressions for organic tiles.
impressionStats = (
<TopSiteImpressionWrapper
actionType={at.TOP_SITES_ORGANIC_IMPRESSION_STATS}
tile={{
source: NEWTAB_SOURCE,
}}
// For testing.
IntersectionObserver={this.props.IntersectionObserver}
document={this.props.document}
dispatch={this.props.dispatch}
/>
);
}

return (
<li
className={topSiteOuterClassName}
Expand Down Expand Up @@ -342,34 +394,7 @@ export class TopSiteLink extends React.PureComponent {
</div>
</a>
{children}
{link.type === SPOC_TYPE ? (
<ImpressionStats
flightId={link.flightId}
rows={[
{
id: link.id,
pos: link.pos,
shim: link.shim && link.shim.impression,
advertiser: title.toLocaleLowerCase(),
},
]}
dispatch={this.props.dispatch}
source={TOP_SITES_SOURCE}
/>
) : null}
{/* Set up an impression wrapper for the sponsored TopSite */}
{link.sponsored_position ? (
<TopSiteImpressionWrapper
tile={{
position: this.props.index + 1,
tile_id: link.sponsored_tile_id || -1,
reporting_url: link.sponsored_impression_url,
advertiser: title.toLocaleLowerCase(),
source: NEWTAB_SOURCE,
}}
dispatch={this.props.dispatch}
/>
) : null}
{impressionStats}
</div>
</li>
);
Expand Down Expand Up @@ -441,9 +466,8 @@ export class TopSite extends React.PureComponent {
})
);

// Fire off a spoc specific impression.
if (this.props.link.type === SPOC_TYPE) {
// Record a Pocket click.
// Record a Pocket-specific click.
this.props.dispatch(
ac.ImpressionStats({
source: TOP_SITES_SOURCE,
Expand All @@ -458,11 +482,11 @@ export class TopSite extends React.PureComponent {
})
);

// Record a click for sponsored topsites.
// Record a click for a Pocket sponsored tile.
const title = this.props.link.label || this.props.link.hostname;
this.props.dispatch(
ac.OnlyToMain({
type: at.TOP_SITES_IMPRESSION_STATS,
type: at.TOP_SITES_SPONSORED_IMPRESSION_STATS,
data: {
type: "click",
position: this.props.link.pos + 1,
Expand All @@ -472,34 +496,46 @@ export class TopSite extends React.PureComponent {
},
})
);
}
if (this.props.link.sendAttributionRequest) {
} else if (isSponsored(this.props.link)) {
// Record a click for a non-Pocket sponsored tile.
const title = this.props.link.label || this.props.link.hostname;
this.props.dispatch(
ac.OnlyToMain({
type: at.PARTNER_LINK_ATTRIBUTION,
type: at.TOP_SITES_SPONSORED_IMPRESSION_STATS,
data: {
targetURL: this.props.link.url,
source: "newtab",
type: "click",
position: this.props.index + 1,
tile_id: this.props.link.sponsored_tile_id || -1,
reporting_url: this.props.link.sponsored_click_url,
advertiser: title.toLocaleLowerCase(),
source: NEWTAB_SOURCE,
},
})
);
}
if (this.props.link.sponsored_position) {
const title = this.props.link.label || this.props.link.hostname;
} else {
// Record a click for an organic tile.
this.props.dispatch(
ac.OnlyToMain({
type: at.TOP_SITES_IMPRESSION_STATS,
type: at.TOP_SITES_ORGANIC_IMPRESSION_STATS,
data: {
type: "click",
position: this.props.index + 1,
tile_id: this.props.link.sponsored_tile_id || -1,
reporting_url: this.props.link.sponsored_click_url,
advertiser: title.toLocaleLowerCase(),
source: NEWTAB_SOURCE,
},
})
);
}

if (this.props.link.sendAttributionRequest) {
this.props.dispatch(
ac.OnlyToMain({
type: at.PARTNER_LINK_ATTRIBUTION,
data: {
targetURL: this.props.link.url,
source: "newtab",
},
})
);
}
} else {
this.props.dispatch(
ac.OnlyToMain({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

import {
actionCreators as ac,
actionTypes as at,
} from "common/Actions.sys.mjs";
import { actionCreators as ac } from "common/Actions.sys.mjs";
import React from "react";

const VISIBLE = "visible";
Expand All @@ -27,11 +24,14 @@ export const INTERSECTION_RATIO = 0.5;
*/
export class TopSiteImpressionWrapper extends React.PureComponent {
_dispatchImpressionStats() {
const { tile } = this.props;
const { actionType, tile } = this.props;
if (!actionType) {
return;
}

this.props.dispatch(
ac.OnlyToMain({
type: at.TOP_SITES_IMPRESSION_STATS,
type: actionType,
data: {
type: "impression",
...tile,
Expand Down Expand Up @@ -144,5 +144,6 @@ export class TopSiteImpressionWrapper extends React.PureComponent {
TopSiteImpressionWrapper.defaultProps = {
IntersectionObserver: global.IntersectionObserver,
document: global.document,
actionType: null,
tile: null,
};
Loading

0 comments on commit a82057d

Please sign in to comment.