1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5import { actionCreators as ac } from "common/Actions.jsm";
6import { ContextMenu } from "content-src/components/ContextMenu/ContextMenu";
7import React from "react";
8import { connect } from "react-redux";
9import { SectionMenuOptions } from "content-src/lib/section-menu-options";
10
11const DEFAULT_SECTION_MENU_OPTIONS = [
12  "MoveUp",
13  "MoveDown",
14  "Separator",
15  "RemoveSection",
16  "CheckCollapsed",
17  "Separator",
18  "ManageSection",
19];
20const WEBEXT_SECTION_MENU_OPTIONS = [
21  "MoveUp",
22  "MoveDown",
23  "Separator",
24  "CheckCollapsed",
25  "Separator",
26  "ManageWebExtension",
27];
28
29export class _SectionMenu extends React.PureComponent {
30  handleAddWhileCollapsed() {
31    const { action, userEvent } = SectionMenuOptions.ExpandSection(this.props);
32    this.props.dispatch(action);
33    if (userEvent) {
34      this.props.dispatch(
35        ac.UserEvent({
36          event: userEvent,
37          source: this.props.source,
38        })
39      );
40    }
41  }
42
43  getOptions() {
44    const { props } = this;
45
46    const propOptions = props.isWebExtension
47      ? [...WEBEXT_SECTION_MENU_OPTIONS]
48      : [...DEFAULT_SECTION_MENU_OPTIONS];
49
50    // Remove Collapse/Expand related option if the `newNewtabExperience.enabled`
51    // pref is set to true.
52    if (props.Prefs.values.featureConfig.newNewtabExperienceEnabled) {
53      if (props.isWebExtension) {
54        propOptions.splice(2, 2);
55      } else {
56        propOptions.splice(4, 1);
57      }
58    }
59
60    // Remove the move related options if the section is fixed
61    if (props.isFixed) {
62      propOptions.splice(propOptions.indexOf("MoveUp"), 3);
63    }
64    // Prepend custom options and a separator
65    if (props.extraOptions) {
66      propOptions.splice(0, 0, ...props.extraOptions, "Separator");
67    }
68    // Insert privacy notice before the last option ("ManageSection")
69    if (props.privacyNoticeURL) {
70      propOptions.splice(-1, 0, "PrivacyNotice");
71    }
72
73    const options = propOptions
74      .map(o => SectionMenuOptions[o](props))
75      .map(option => {
76        const { action, id, type, userEvent } = option;
77        if (!type && id) {
78          option.onClick = () => {
79            const hasAddEvent =
80              userEvent === "MENU_ADD_TOPSITE" ||
81              userEvent === "MENU_ADD_SEARCH";
82
83            if (props.collapsed && hasAddEvent) {
84              this.handleAddWhileCollapsed();
85            }
86
87            props.dispatch(action);
88            if (userEvent) {
89              props.dispatch(
90                ac.UserEvent({
91                  event: userEvent,
92                  source: props.source,
93                })
94              );
95            }
96          };
97        }
98        return option;
99      });
100
101    // This is for accessibility to support making each item tabbable.
102    // We want to know which item is the first and which item
103    // is the last, so we can close the context menu accordingly.
104    options[0].first = true;
105    options[options.length - 1].last = true;
106    return options;
107  }
108
109  render() {
110    return (
111      <ContextMenu
112        onUpdate={this.props.onUpdate}
113        options={this.getOptions()}
114        keyboardAccess={this.props.keyboardAccess}
115      />
116    );
117  }
118}
119
120export const SectionMenu = connect(state => ({
121  Prefs: state.Prefs,
122}))(_SectionMenu);
123