summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CollectionCardGrid/CollectionCardGrid.jsx
blob: d089a5c8abd7de16e76921e8057e55110a1a29cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* 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 { actionCreators as ac } from "common/Actions.sys.mjs";
import { CardGrid } from "content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid";
import { DSDismiss } from "content-src/components/DiscoveryStreamComponents/DSDismiss/DSDismiss";
import { LinkMenuOptions } from "content-src/lib/link-menu-options";
import React from "react";

export class CollectionCardGrid extends React.PureComponent {
  constructor(props) {
    super(props);
    this.onDismissClick = this.onDismissClick.bind(this);
    this.state = {
      dismissed: false,
    };
  }

  onDismissClick() {
    const { data } = this.props;
    if (this.props.dispatch && data && data.spocs && data.spocs.length) {
      this.setState({
        dismissed: true,
      });
      const pos = 0;
      const source = this.props.type.toUpperCase();
      // Grab the available items in the array to dismiss.
      // This fires a ping for all items available, even if below the fold.
      const spocsData = data.spocs.map(item => ({
        url: item.url,
        guid: item.id,
        shim: item.shim,
        flight_id: item.flightId,
      }));

      const blockUrlOption = LinkMenuOptions.BlockUrls(spocsData, pos, source);
      const { action, impression, userEvent } = blockUrlOption;
      this.props.dispatch(action);

      this.props.dispatch(
        ac.DiscoveryStreamUserEvent({
          event: userEvent,
          source,
          action_position: pos,
        })
      );
      if (impression) {
        this.props.dispatch(impression);
      }
    }
  }

  render() {
    const { data, dismissible, pocket_button_enabled } = this.props;
    if (
      this.state.dismissed ||
      !data ||
      !data.spocs ||
      !data.spocs[0] ||
      // We only display complete collections.
      data.spocs.length < 3
    ) {
      return null;
    }
    const { spocs, placement, feed } = this.props;
    // spocs.data is spocs state data, and not an array of spocs.
    const { title, context, sponsored_by_override, sponsor } =
      spocs.data[placement.name] || {};
    // Just in case of bad data, don't display a broken collection.
    if (!title) {
      return null;
    }

    let sponsoredByMessage = "";

    // If override is not false or an empty string.
    if (sponsored_by_override || sponsored_by_override === "") {
      // We specifically want to display nothing if the server returns an empty string.
      // So the server can turn off the label.
      // This is to support the use cases where the sponsored context is displayed elsewhere.
      sponsoredByMessage = sponsored_by_override;
    } else if (sponsor) {
      sponsoredByMessage = {
        id: `newtab-label-sponsored-by`,
        values: { sponsor },
      };
    } else if (context) {
      sponsoredByMessage = context;
    }

    // Generally a card grid displays recs with spocs already injected.
    // Normally it doesn't care which rec is a spoc and which isn't,
    // it just displays content in a grid.
    // For collections, we're only displaying a list of spocs.
    // We don't need to tell the card grid that our list of cards are spocs,
    // it shouldn't need to care. So we just pass our spocs along as recs.
    // Think of it as injecting all rec positions with spocs.
    // Consider maybe making recommendations in CardGrid use a more generic name.
    const recsData = {
      recommendations: data.spocs,
    };

    // All cards inside of a collection card grid have a slightly different type.
    // For the case of interactions to the card grid, we use the type "COLLECTIONCARDGRID".
    // Example, you dismiss the whole collection, we use the type "COLLECTIONCARDGRID".
    // For interactions inside the card grid, example, you dismiss a single card in the collection,
    // we use the type "COLLECTIONCARDGRID_CARD".
    const type = `${this.props.type}_card`;

    const collectionGrid = (
      <div className="ds-collection-card-grid">
        <CardGrid
          pocket_button_enabled={pocket_button_enabled}
          title={title}
          context={sponsoredByMessage}
          data={recsData}
          feed={feed}
          type={type}
          is_collection={true}
          dispatch={this.props.dispatch}
          items={this.props.items}
        />
      </div>
    );

    if (dismissible) {
      return (
        <DSDismiss
          onDismissClick={this.onDismissClick}
          extraClasses={`ds-dismiss-ds-collection`}
        >
          {collectionGrid}
        </DSDismiss>
      );
    }
    return collectionGrid;
  }
}