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

mozilla / fx-private-relay / f7bc8e83-3560-4369-be54-50d9795958aa

06 Oct 2025 01:05PM UTC coverage: 88.879% (+0.02%) from 88.863%
f7bc8e83-3560-4369-be54-50d9795958aa

Pull #5924

circleci

web-flow
Merge pull request #5941 from mozilla/MPP-4348-propagate-utm-params-hydration

Fix hydration errors
Pull Request #5924: MPP-4348: propagate UTM params from landing page to SubPlat and Accounts URLs

2925 of 3933 branches covered (74.37%)

Branch coverage included in aggregate %.

37 of 43 new or added lines in 12 files covered. (86.05%)

1 existing line in 1 file now uncovered.

18110 of 19734 relevant lines covered (91.77%)

13.22 hits per line

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

84.04
/frontend/src/pages/phone.page.tsx
1
import { NextPage } from "next";
2
import { Layout } from "../components/layout/Layout";
1✔
3
import { useProfiles } from "../hooks/api/profile";
1✔
4
import { useUsers } from "../hooks/api/user";
1✔
5
import { PhoneOnboarding } from "../components/phones/onboarding/PhoneOnboarding";
1✔
6
import { useRelayNumber } from "../hooks/api/relayNumber";
1✔
7
import { useEffect, useState } from "react";
1✔
8
import { PhoneDashboard } from "../components/phones/dashboard/PhoneDashboard";
1✔
9
import { getRuntimeConfig } from "../config";
1✔
10
import { PurchasePhonesPlan } from "../components/phones/onboarding/PurchasePhonesPlan";
1✔
11
import { isVerified, useRealPhonesData } from "../hooks/api/realPhone";
1✔
12
import { DashboardSwitcher } from "../components/layout/navigation/DashboardSwitcher";
1✔
13
import { isFlagActive } from "../functions/waffle";
1✔
14
import { useRuntimeData } from "../hooks/api/runtimeData";
1✔
15
import { useRouter } from "next/router";
1✔
16
import { isPhonesAvailableInCountry } from "../functions/getPlan";
1✔
17
import { PhoneWelcomeView } from "../components/phones/dashboard/PhoneWelcomeView";
1✔
18
import { useLocalDismissal } from "../hooks/localDismissal";
1✔
19
import { useUtmApplier } from "../hooks/utmApplier";
1✔
20

21
const Phone: NextPage = () => {
1✔
22
  const runtimeData = useRuntimeData();
10✔
23
  const profileData = useProfiles();
10✔
24
  const profile = profileData.data?.[0];
10✔
25
  const router = useRouter();
10✔
26
  const userData = useUsers();
10✔
27
  const user = userData.data?.[0];
10✔
28
  const relayNumberData = useRelayNumber();
10✔
29
  const [isInOnboarding, setIsInOnboarding] = useState<boolean>();
10✔
30
  const welcomeScreenDismissal = useLocalDismissal(
10✔
31
    `phone-welcome-screen-${profile?.id}`,
32
  );
33

34
  const resendWelcomeSMSDismissal = useLocalDismissal(
10✔
35
    `resend-sms-banner-${profile?.id}`,
36
  );
37

38
  const realPhoneData = useRealPhonesData();
10✔
39
  // The user hasn't completed the onboarding yet if...
40
  const isNotSetup =
41
    // ...they haven't purchased the phone plan yet, or...
42
    profile?.has_phone === false ||
10✔
43
    // ...the API request for their Relay number has completed but returned no
44
    // result, or...
45
    (!relayNumberData.isValidating &&
46
      typeof relayNumberData.error === "undefined" &&
47
      typeof relayNumberData.data === "undefined") ||
48
    // ...there was a list of Relay numbers, but it was empty:
49
    relayNumberData.data?.length === 0;
50

51
  useEffect(() => {
10✔
52
    if (!runtimeData.data) {
10!
53
      return;
×
54
    }
55
    if (
10✔
56
      // Send the user to /premium if a phone subscription is not available in
57
      // the current country, and the user has not set it up before (possibly in
58
      // another country):
59
      !isPhonesAvailableInCountry(runtimeData.data) &&
20✔
60
      isNotSetup
61
    ) {
62
      router.push("/premium");
6✔
63
    }
64
  }, [runtimeData.data, router, isNotSetup]);
65

66
  useEffect(() => {
10✔
67
    if (
8✔
68
      typeof isInOnboarding === "undefined" &&
18✔
69
      Array.isArray(relayNumberData.data) &&
70
      relayNumberData.data.length === 0
71
    ) {
72
      setIsInOnboarding(true);
3✔
73
    }
74
  }, [isInOnboarding, relayNumberData]);
75

76
  const applyUtmParams = useUtmApplier();
10✔
77
  if (!userData.isValidating && userData.error) {
10!
NEW
78
    document.location.assign(applyUtmParams(getRuntimeConfig().fxaLoginUrl));
×
79
  }
80

81
  if (!profile || !user || !relayNumberData.data || !runtimeData.data) {
10!
82
    // TODO: Show a loading spinner?
83
    return null;
×
84
  }
85

86
  // If the user has their phone subscription all set up, show the dashboard:
87
  const verifiedPhones = realPhoneData.data?.filter(isVerified) ?? [];
10!
88
  if (
10✔
89
    profile.has_phone &&
24✔
90
    !isInOnboarding &&
91
    verifiedPhones.length > 0 &&
92
    relayNumberData.data.length > 0
93
  ) {
94
    return (
95
      <Layout runtimeData={runtimeData.data}>
96
        <DashboardSwitcher />
97
        {/* Only show the welcome screen if the user hasn't seen it before */}
98
        {!welcomeScreenDismissal.isDismissed &&
8✔
99
        isFlagActive(runtimeData.data, "multi_replies") ? (
100
          <PhoneWelcomeView
×
101
            dismissal={{
102
              welcomeScreen: welcomeScreenDismissal,
103
              resendSMS: resendWelcomeSMSDismissal,
104
            }}
105
            onRequestContactCard={() => realPhoneData.resendWelcomeSMS()}
×
106
            profile={profile}
107
          />
108
        ) : (
109
          <PhoneDashboard
110
            dismissal={{
111
              resendSMS: resendWelcomeSMSDismissal,
112
            }}
113
            profile={profile}
114
            runtimeData={runtimeData.data}
115
            realPhone={verifiedPhones[0]}
116
            onRequestContactCard={() => realPhoneData.resendWelcomeSMS()}
×
117
          />
118
        )}
119
      </Layout>
120
    );
121
  }
122

123
  // If the user doesn't have a phone subscription already set up and can't
124
  // buy it in the country they're in, don't render anything; the `useEffect`
125
  // above will redirect the user to /premium
126
  if (!isPhonesAvailableInCountry(runtimeData.data)) {
6!
127
    return null;
6✔
128
  }
129

130
  // show the phone plan purchase page if the user has not purchased phone product
131
  if (!profile.has_phone) {
×
132
    return (
133
      <Layout runtimeData={runtimeData.data}>
134
        <DashboardSwitcher />
135
        <PurchasePhonesPlan runtimeData={runtimeData.data} />
136
      </Layout>
137
    );
138
  }
139

140
  // Otherwise start the onboarding process
141
  return (
142
    <Layout runtimeData={runtimeData.data}>
143
      <DashboardSwitcher />
144
      <PhoneOnboarding
145
        onComplete={() => setIsInOnboarding(false)}
×
146
        profile={profile}
147
        runtimeData={runtimeData.data}
148
      />
149
    </Layout>
150
  );
151
};
152

153
export default Phone;
5✔
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