diff --git a/common/Reducers.jsm b/common/Reducers.jsm index a2e5374ca4..8ed2c46fa9 100644 --- a/common/Reducers.jsm +++ b/common/Reducers.jsm @@ -63,6 +63,7 @@ const INITIAL_STATE = { spocs: { spocs_endpoint: "", lastUpdated: null, + showSpocs: false, data: {}, // {spocs: []} loaded: false, frequency_caps: [], @@ -606,6 +607,7 @@ function DiscoveryStream(prevState = INITIAL_STATE.DiscoveryStream, action) { ...prevState, spocs: { ...prevState.spocs, + showSpocs: action.data.showSpocs, lastUpdated: action.data.lastUpdated, data: action.data.spocs, loaded: true, diff --git a/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx b/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx index 6c8c1103ec..4bf93fe96f 100644 --- a/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx +++ b/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx @@ -6,6 +6,7 @@ import { actionCreators as ac } from "common/Actions.jsm"; import { CardGrid } from "content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid"; import { CollapsibleSection } from "content-src/components/CollapsibleSection/CollapsibleSection"; import { connect } from "react-redux"; +import { DSDismiss } from "content-src/components/DiscoveryStreamComponents/DSDismiss/DSDismiss"; import { DSMessage } from "content-src/components/DiscoveryStreamComponents/DSMessage/DSMessage"; import { Hero } from "content-src/components/DiscoveryStreamComponents/Hero/Hero"; import { Highlights } from "content-src/components/DiscoveryStreamComponents/Highlights/Highlights"; @@ -301,11 +302,8 @@ export class _DiscoveryStreamBase extends React.PureComponent { const styles = []; return (
- {layoutRender.map((row, rowIndex) => ( -
+ {layoutRender.map((row, rowIndex) => { + const contents = (
{row.components.map((component, componentIndex) => { if (!component) { @@ -315,6 +313,14 @@ export class _DiscoveryStreamBase extends React.PureComponent { ...(styles[rowIndex] || []), component.styles, ]; + // TODO make this dry + if (component.campaign_id) { + return ( + + {this.renderComponent(component, row.width)} + + ); + } return (
{this.renderComponent(component, row.width)} @@ -322,8 +328,29 @@ export class _DiscoveryStreamBase extends React.PureComponent { ); })}
-
- ))} + ); + // TODO Dry this out too. + if (row.campaign_id) { + return ( +
+ + {contents} + +
+ ); + } + return ( +
+ {contents} +
+ ); + })} {this.renderStyles(styles)}
); diff --git a/content-src/components/DiscoveryStreamComponents/DSDismiss/DSDismiss.jsx b/content-src/components/DiscoveryStreamComponents/DSDismiss/DSDismiss.jsx new file mode 100644 index 0000000000..30ae72c2ee --- /dev/null +++ b/content-src/components/DiscoveryStreamComponents/DSDismiss/DSDismiss.jsx @@ -0,0 +1,21 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 React from "react"; + +export class DSDismiss extends React.PureComponent { + render() { + // TODO: + // This needs an x button that dismisses the campaign_id passed to it. + // It also needs a hover state for it and its children. + // Somewhere else, we need to filter out anything with a campaign_id that we've blocked. + // Consider calling this collection_id. + // Right now it is this.props.campaignId + return ( +
+ {this.props.children} +
+ ); + } +} diff --git a/content-src/components/DiscoveryStreamComponents/DSDismiss/_DSDismiss.scss b/content-src/components/DiscoveryStreamComponents/DSDismiss/_DSDismiss.scss new file mode 100644 index 0000000000..5ef67839f1 --- /dev/null +++ b/content-src/components/DiscoveryStreamComponents/DSDismiss/_DSDismiss.scss @@ -0,0 +1,4 @@ +.ds-dismiss { + background: lightgreen;; + border-radius: 5px; +} diff --git a/content-src/components/DiscoveryStreamComponents/DSMessage/DSMessage.jsx b/content-src/components/DiscoveryStreamComponents/DSMessage/DSMessage.jsx index 91a46d4c08..fb591b297a 100644 --- a/content-src/components/DiscoveryStreamComponents/DSMessage/DSMessage.jsx +++ b/content-src/components/DiscoveryStreamComponents/DSMessage/DSMessage.jsx @@ -25,6 +25,7 @@ export class DSMessage extends React.PureComponent { )} + {this.props.subtitle && (
{this.props.subtitle}
)}
); } diff --git a/content-src/components/DiscoveryStreamComponents/DSMessage/_DSMessage.scss b/content-src/components/DiscoveryStreamComponents/DSMessage/_DSMessage.scss index 41b4c3863b..2524731f52 100644 --- a/content-src/components/DiscoveryStreamComponents/DSMessage/_DSMessage.scss +++ b/content-src/components/DiscoveryStreamComponents/DSMessage/_DSMessage.scss @@ -42,4 +42,10 @@ } } } + + .subtitle { + font-size: 13px; + line-height: 24px; + color: $grey-50; + } } diff --git a/content-src/lib/selectLayoutRender.js b/content-src/lib/selectLayoutRender.js index a83b71d047..0cc9abfa6e 100644 --- a/content-src/lib/selectLayoutRender.js +++ b/content-src/lib/selectLayoutRender.js @@ -139,19 +139,29 @@ export const selectLayoutRender = (state, prefs, rickRollCache) => { return { ...component, data }; }; + const filterComponent = (c) => { + return ( + !filterArray.includes(c.type) && + (!c.sponsored || spocs.showSpocs) + ); + }; + + const filterRow = (r) => { + return ( + r.components.filter(filterComponent).length && + (!r.sponsored || spocs.showSpocs) + ); + }; + const renderLayout = () => { const renderedLayoutArray = []; - for (const row of layout.filter( - r => r.components.filter(c => !filterArray.includes(c.type)).length - )) { + for (const row of layout.filter(filterRow)) { let components = []; renderedLayoutArray.push({ ...row, components, }); - for (const component of row.components.filter( - c => !filterArray.includes(c.type) - )) { + for (const component of row.components.filter(filterComponent)) { if (component.feed) { const spocsConfig = component.spocs; // Are we still waiting on a feed/spocs, render what we have, diff --git a/content-src/styles/_activity-stream.scss b/content-src/styles/_activity-stream.scss index 821b1c3e6a..6636b53859 100644 --- a/content-src/styles/_activity-stream.scss +++ b/content-src/styles/_activity-stream.scss @@ -158,6 +158,7 @@ input { @import '../components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu'; @import '../components/DiscoveryStreamComponents/DSCard/DSCard'; @import '../components/DiscoveryStreamComponents/DSImage/DSImage'; +@import '../components/DiscoveryStreamComponents/DSDismiss/DSDismiss'; @import '../components/DiscoveryStreamComponents/DSMessage/DSMessage'; @import '../components/DiscoveryStreamImpressionStats/ImpressionStats'; @import '../components/DiscoveryStreamComponents/DSEmptyState/DSEmptyState'; diff --git a/lib/DiscoveryStreamFeed.jsm b/lib/DiscoveryStreamFeed.jsm index 78dc9b4ca1..bcbb942761 100644 --- a/lib/DiscoveryStreamFeed.jsm +++ b/lib/DiscoveryStreamFeed.jsm @@ -527,6 +527,7 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed { sendUpdate({ type: at.DISCOVERY_STREAM_SPOCS_UPDATE, data: { + showSpocs: this.showSpocs, lastUpdated: spocs.lastUpdated, spocs: newSpocs, }, @@ -1286,10 +1287,90 @@ defaultLayoutResp = { { width: 12, components: [ + { + type: "Message", + header: { + title: "Not Sponsored by Collection Title", + subtitle: "Not Sponsored by Brand Name", + }, + properties: {}, + }, + { + type: "Message", + header: { + title: "Sponsored by Collection Title", + subtitle: "Sponsored by Brand Name", + }, + campaign_id: "123456", + sponsored: true, + properties: {}, + }, + ], + }, + { + width: 12, + campaign_id: "123456", + components: [ + { + type: "Message", + header: { + title: "campaign but not paid", + subtitle: "campaign but not paid", + }, + properties: {}, + }, + { + type: "Message", + header: { + title: "campaign but not paid", + subtitle: "campaign but not paid", + }, + properties: {}, + }, + ], + }, + { + width: 12, + components: [ + { + type: "Message", + campaign_id: "123456", + header: { + title: "campaign but not paid", + subtitle: "campaign but not paid", + }, + properties: {}, + }, + { + type: "Message", + header: { + title: "not campaign but not paid", + subtitle: "not campaign but not paid", + }, + properties: {}, + }, + ], + }, + { + width: 12, + campaign_id: "123456", + sponsored: true, + components: [ + { + type: "Message", + header: { + title: "Sponsored by Collection Title", + subtitle: "Sponsored by Brand Name", + }, + properties: {}, + styles: { + ".ds-message": "margin-bottom: -28px", + }, + }, { type: "CardGrid", properties: { - items: 21, + items: 3, }, header: { title: "", @@ -1299,22 +1380,20 @@ defaultLayoutResp = { url: "https://getpocket.cdn.mozilla.net/v3/firefox/global-recs?version=3&consumer_key=$apiKey&locale_lang=en-US&count=30", }, - spocs: { - probability: 1, - positions: [ - { - index: 2, - }, - { - index: 4, - }, - { - index: 11, - }, - { - index: 20, - }, - ], + }, + { + type: "Hero", + properties: { + items: 5, + offset: 3, + }, + header: { + title: "", + }, + feed: { + embed_reference: null, + url: + "https://getpocket.cdn.mozilla.net/v3/firefox/global-recs?version=3&consumer_key=$apiKey&locale_lang=en-US&count=30", }, }, {