• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / fx-private-relay / a2bc0383-1205-4ebd-979f-e7ee6dba9a0d

18 Dec 2023 05:15PM UTC coverage: 73.514% (-0.7%) from 74.258%
a2bc0383-1205-4ebd-979f-e7ee6dba9a0d

push

circleci

jwhitlock
Add provider_id="" to SocialApp init

1962 of 2913 branches covered (0.0%)

Branch coverage included in aggregate %.

6273 of 8289 relevant lines covered (75.68%)

19.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.31
/frontend/src/components/dashboard/ProfileBanners.tsx
1
import { ReactNode } from "react";
2
import { event as gaEvent } from "react-ga";
1✔
3

4
import Image from "next/image";
1✔
5
import styles from "./ProfileBanners.module.scss";
1✔
6
import FirefoxLogo from "./images/fx-logo.svg";
1✔
7
import BundleLogo from "./images/vpn-and-relay-logo.svg";
1✔
8
import AddonIllustration from "./images/banner-addon.svg";
1✔
9
import {
10
  getBundlePrice,
11
  isBundleAvailableInCountry,
12
  RuntimeDataWithBundleAvailable,
13
} from "../../functions/getPlan";
1✔
14
import {
15
  isUsingFirefox,
16
  supportsChromeExtension,
17
  supportsFirefoxExtension,
18
} from "../../functions/userAgent";
1✔
19
import { ProfileData } from "../../hooks/api/profile";
20
import { UserData } from "../../hooks/api/user";
21
import { RuntimeData } from "../../../src/hooks/api/runtimeData";
22
import { Banner } from "../Banner";
1✔
23
import { renderDate } from "../../functions/renderDate";
1✔
24
import { SubdomainPicker } from "./SubdomainPicker";
1✔
25
import { useMinViewportWidth } from "../../hooks/mediaQuery";
1✔
26
import { AliasData } from "../../hooks/api/aliases";
27
import { useL10n } from "../../hooks/l10n";
1✔
28

29
export type Props = {
30
  profile: ProfileData;
31
  user: UserData;
32
  onCreateSubdomain: (chosenSubdomain: string) => Promise<void>;
33
  runtimeData?: RuntimeData;
34
  aliases: AliasData[];
35
};
36

37
/**
38
 * Displays relevant banners for on the user's profile page.
39
 *
40
 * Examples are:
41
 * - A banner to download Firefox if you're not on Firefox.
42
 * - A banner to download the add-on if you don't have it.
43
 * - A banner to upgrade to Premium if you don't have it but can purchase it.
44
 * - A banner to inform the user of emails that failed to be delivered.
45
 * - Anything else we might come up with in the future.
46
 *
47
 * See also {@link Banner}.
48
 */
49
export const ProfileBanners = (props: Props) => {
46✔
50
  const banners: ReactNode[] = [];
46✔
51
  const isLargeScreen = useMinViewportWidth("md");
46✔
52

53
  const bounceStatus = props.profile.bounce_status;
46✔
54
  if (bounceStatus[0]) {
46!
55
    banners.push(
×
56
      <BounceBanner
57
        key="bounce-banner"
58
        email={props.user.email}
59
        profile={props.profile}
60
      />,
61
    );
62
  }
63

64
  if (isBundleAvailableInCountry(props.runtimeData) && !props.profile.has_vpn) {
46!
65
    banners.push(
×
66
      <BundlePromoBanner
67
        key="bundle-promo"
68
        runtimeData={props.runtimeData}
69
        profileData={props.profile}
70
      />,
71
    );
72
  }
73

74
  banners.push(
46✔
75
    <SubdomainPicker
76
      key="subdomain-picker"
77
      profile={props.profile}
78
      onCreate={props.onCreateSubdomain}
79
    />,
80
  );
81

82
  // Don't show the "Get Firefox" banner if we have an extension available,
83
  // to avoid banner overload:
84
  if (!isUsingFirefox() && (!supportsChromeExtension() || !isLargeScreen)) {
46✔
85
    banners.push(<NoFirefoxBanner key="firefox-banner" />);
41✔
86
  }
87

88
  if (
46✔
89
    supportsFirefoxExtension() &&
53✔
90
    isLargeScreen &&
91
    // This identifies mock data used for demonstration purposes.
92
    // See /frontend/src/apiMocks/mockData.ts:
93
    props.profile.api_token !== "demo"
94
  ) {
95
    // This pushes a banner promoting the add-on - detecting the add-on
96
    // and determining whether to show it based on that is a bit slow,
97
    // so we'll just let the add-on hide it:
98
    banners.push(
3✔
99
      <NoAddonBanner profileData={props.profile} key="addon-banner" />,
100
    );
101
  }
102

103
  if (supportsChromeExtension() && isLargeScreen) {
46✔
104
    // This pushes a banner promoting the add-on - detecting the add-on
105
    // and determining whether to show it based on that is a bit slow,
106
    // so we'll just let the add-on hide it:
107
    banners.push(
1✔
108
      <NoChromeExtensionBanner
109
        profileData={props.profile}
110
        key="chrome-extension-banner"
111
      />,
112
    );
113
  }
114

115
  return <div className={styles["profile-banners"]}>{banners}</div>;
116
};
117

118
type BounceBannerProps = {
119
  email: string;
120
  profile: ProfileData;
121
};
122
const BounceBanner = (props: BounceBannerProps) => {
1✔
123
  const l10n = useL10n();
×
124

125
  return (
126
    <Banner type="warning" title={l10n.getString("banner-bounced-headline")}>
127
      {l10n.getFragment("banner-bounced-copy", {
128
        vars: {
129
          username: props.email,
130
          bounce_type: props.profile.bounce_status[1],
131
          date: renderDate(props.profile.next_email_try, l10n),
132
        },
133
        elems: {
134
          em: <em />,
135
        },
136
      })}
137
    </Banner>
138
  );
139
};
140

141
const NoFirefoxBanner = () => {
1✔
142
  const l10n = useL10n();
41✔
143

144
  return (
145
    <Banner
146
      type="promo"
147
      title={l10n.getString("banner-download-firefox-headline")}
148
      illustration={{
149
        img: <Image src={FirefoxLogo} alt="" width={60} height={60} />,
150
      }}
151
      cta={{
152
        target:
153
          "https://www.mozilla.org/firefox/new/?utm_source=fx-relay&utm_medium=banner&utm_campaign=download-fx",
154
        content: l10n.getString("banner-download-firefox-cta"),
155
        size: "large",
156
        gaViewPing: {
157
          category: "Download Firefox",
158
          label: "profile-banner-download-firefox",
159
        },
160
        onClick: () => {
161
          gaEvent({
×
162
            category: "Download Firefox",
163
            action: "Engage",
164
            label: "profile-banner-download-firefox",
165
          });
166
        },
167
      }}
168
    >
169
      <p>{l10n.getString("banner-download-firefox-copy-2")}</p>
170
    </Banner>
171
  );
172
};
173

174
type NoAddonBannerProps = {
175
  profileData: ProfileData;
176
};
177

178
const NoAddonBanner = (props: NoAddonBannerProps) => {
1✔
179
  const l10n = useL10n();
3✔
180

181
  return (
182
    <Banner
183
      type="promo"
184
      title={l10n.getString("banner-download-install-extension-headline")}
185
      illustration={{
186
        img: <Image src={AddonIllustration} alt="" width={60} height={60} />,
187
      }}
188
      cta={{
189
        target:
190
          "https://addons.mozilla.org/firefox/addon/private-relay/?utm_source=fx-relay&utm_medium=banner&utm_campaign=install-addon",
191
        content: l10n.getString("banner-download-install-extension-cta"),
192
        size: "large",
193
        gaViewPing: {
194
          category: "Download Extension",
195
          label: "profile-banner-download-firefox-extension",
196
        },
197
        onClick: () => {
198
          gaEvent({
×
199
            category: "Download Firefox",
200
            action: "Engage",
201
            label: "profile-banner-download-firefox-extension",
202
          });
203
        },
204
      }}
205
      dismissal={{
206
        key: `firefox-extension-banner-${props.profileData.id}`,
207
      }}
208
      hiddenWithAddon={true}
209
    >
210
      <p>{l10n.getString("banner-download-install-extension-copy-2")}</p>
211
    </Banner>
212
  );
213
};
214

215
type NoChromeExtensionBannerProps = {
216
  profileData: ProfileData;
217
};
218

219
// make dismissble
220
const NoChromeExtensionBanner = (props: NoChromeExtensionBannerProps) => {
1✔
221
  const l10n = useL10n();
1✔
222

223
  return (
224
    <Banner
225
      type="promo"
226
      title={l10n.getString(
227
        "banner-download-install-chrome-extension-headline",
228
      )}
229
      illustration={{
230
        img: <Image src={AddonIllustration} alt="" width={60} height={60} />,
231
      }}
232
      cta={{
233
        target:
234
          "https://chrome.google.com/webstore/detail/firefox-relay/lknpoadjjkjcmjhbjpcljdednccbldeb?utm_source=fx-relay&utm_medium=banner&utm_campaign=install-addon",
235
        content: l10n.getString("banner-download-install-chrome-extension-cta"),
236
        size: "large",
237
        gaViewPing: {
238
          category: "Download Extension",
239
          label: "profile-banner-download-chrome-extension",
240
        },
241
        onClick: () => {
242
          gaEvent({
×
243
            category: "Download Firefox",
244
            action: "Engage",
245
            label: "profile-banner-download-chrome-extension",
246
          });
247
        },
248
      }}
249
      hiddenWithAddon={true}
250
      dismissal={{
251
        key: `no-chrome-extension-banner-${props.profileData.id}`,
252
      }}
253
    >
254
      <p>{l10n.getString("banner-download-install-chrome-extension-copy-2")}</p>
255
    </Banner>
256
  );
257
};
258

259
type BundleBannerProps = {
260
  runtimeData: RuntimeDataWithBundleAvailable;
261
  profileData: ProfileData;
262
};
263

264
const BundlePromoBanner = (props: BundleBannerProps) => {
1✔
265
  const l10n = useL10n();
×
266

267
  return (
×
268
    <Banner
269
      key="bundle-banner"
270
      type="promo"
271
      illustration={{
272
        img: (
273
          <Image
274
            src={BundleLogo}
275
            alt=""
276
            width={120}
277
            height={60}
278
            className={styles["bundle-logo"]}
279
          />
280
        ),
281
      }}
282
      title={l10n.getString("bundle-banner-dashboard-header")}
283
      cta={{
284
        target: "/premium#pricing",
285
        size: "large",
286
        gaViewPing: {
287
          category: "Purchase Bundle button",
288
          label: "profile-banner-bundle-promo",
289
        },
290
        onClick: () => {
291
          gaEvent({
×
292
            category: "Purchase Bundle button",
293
            action: "Engage",
294
            label: "profile-banner-bundle-promo",
295
          });
296
        },
297
        content: l10n.getString("bundle-banner-dashboard-upgrade-cta"),
298
      }}
299
      dismissal={{
300
        key: `bundle-promo-banner-${props.profileData.id}`,
301
      }}
302
    >
303
      <p>
304
        {l10n.getString("bundle-banner-dashboard-body", {
305
          savings: "40%",
306
          monthly_price: getBundlePrice(props.runtimeData, l10n),
307
        })}
308
      </p>
309
    </Banner>
310
  );
311
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc