Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/app/verify-email-banner.tsx
1496 views
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { Modal, Space } from "antd";
7
import { FormattedMessage, useIntl } from "react-intl";
8
9
import { emailVerificationMsg } from "@cocalc/frontend/account/settings/email-verification";
10
import { Button } from "@cocalc/frontend/antd-bootstrap";
11
import {
12
redux,
13
useActions,
14
useAsyncEffect,
15
useState,
16
useTypedRedux,
17
} from "@cocalc/frontend/app-framework";
18
import { getNow } from "@cocalc/frontend/app/util";
19
import { Icon, Paragraph, Text } from "@cocalc/frontend/components";
20
import { labels } from "@cocalc/frontend/i18n";
21
import * as LS from "@cocalc/frontend/misc/local-storage-typed";
22
import { webapp_client } from "@cocalc/frontend/webapp-client";
23
import { once } from "@cocalc/util/async-utils";
24
25
const DISMISSED_KEY_LS = "verify-email-dismissed";
26
27
export function VerifyEmail() {
28
const [hide, setHide] = useState<boolean>(false);
29
const show = useShowVerifyEmail();
30
31
function doDismiss(save = true) {
32
if (save) {
33
const now = getNow();
34
LS.set(DISMISSED_KEY_LS, now);
35
}
36
setHide(true);
37
}
38
39
if (show && !hide) {
40
return <VerifyEmailModal doDismiss={doDismiss} />;
41
} else {
42
return null;
43
}
44
}
45
46
function VerifyEmailModal({
47
doDismiss,
48
}: {
49
doDismiss: (save?: boolean) => void;
50
}) {
51
const intl = useIntl();
52
const page_actions = useActions("page");
53
const email_address = useTypedRedux("account", "email_address");
54
55
const [error, setError] = useState<string>("");
56
const [sending, setSending] = useState<boolean>(false);
57
const [sent, setSent] = useState<boolean>(false);
58
59
async function verify(): Promise<void> {
60
try {
61
setSending(true);
62
await webapp_client.account_client.send_verification_email();
63
} catch (err) {
64
const errMsg = `Problem sending email verification: ${err}`;
65
setError(errMsg);
66
} finally {
67
setSent(true);
68
}
69
}
70
71
// TODO: at one point this should be a popup to just edit the email address
72
function edit() {
73
doDismiss(false);
74
page_actions.set_active_tab("account");
75
}
76
77
function renderBanner() {
78
if (error) {
79
return <Text type="danger">{error}</Text>;
80
}
81
return (
82
<>
83
<Paragraph strong>
84
<FormattedMessage
85
id="app.verify-email-banner.text"
86
defaultMessage={`{sent, select,
87
true {Email Sent! Please check your email inbox (and maybe spam) and click on the confirmation link.}
88
other {Please check and verify your email address:}}`}
89
values={{
90
sent,
91
code: (c) => <Text code>{c}</Text>,
92
}}
93
/>
94
</Paragraph>
95
{!sent ? (
96
<>
97
<Paragraph code style={{ textAlign: "center" }}>
98
{email_address}
99
</Paragraph>
100
<Paragraph type="secondary">
101
<FormattedMessage
102
id="app.verify-email-banner.help.text"
103
defaultMessage="It's important to have a working email address. We use it for password resets, sending messages, billing notifications, and support. Please ensure your email is correct to stay informed."
104
/>
105
</Paragraph>
106
<Paragraph type="secondary">
107
<FormattedMessage
108
id="app.verify-email-banner.edit"
109
defaultMessage="If the email address is wrong, please <E>edit</E> it in the account settings."
110
values={{
111
E: (text) => (
112
<Button onClick={edit} bsSize="xsmall">
113
<Icon name="pencil" /> {text}
114
</Button>
115
),
116
}}
117
/>
118
</Paragraph>
119
</>
120
) : null}
121
</>
122
);
123
}
124
125
function renderFooter() {
126
if (sent) {
127
return (
128
<Button onClick={() => doDismiss()} bsStyle="success">
129
{intl.formatMessage(labels.close)}
130
</Button>
131
);
132
}
133
134
return (
135
<Space>
136
<Button onClick={() => doDismiss()}>
137
{intl.formatMessage(labels.close)}
138
</Button>
139
<Button
140
onClick={verify}
141
bsStyle="success"
142
active={!sent && sending}
143
disabled={sent || sending}
144
>
145
{intl.formatMessage(emailVerificationMsg, {
146
disabled_button: sent,
147
})}
148
</Button>
149
</Space>
150
);
151
}
152
153
function renderTitle() {
154
return (
155
<>
156
<Icon name="mail" />{" "}
157
{intl.formatMessage({
158
id: "app.verify-email-banner.title",
159
defaultMessage: "Verify Your Email Address",
160
})}
161
</>
162
);
163
}
164
165
return (
166
<Modal
167
title={renderTitle()}
168
open={true}
169
onCancel={() => doDismiss()}
170
footer={renderFooter()}
171
>
172
{renderBanner()}
173
</Modal>
174
);
175
}
176
177
export function useShowVerifyEmail(): boolean {
178
const email_address = useTypedRedux("account", "email_address");
179
const email_address_verified = useTypedRedux(
180
"account",
181
"email_address_verified",
182
);
183
const [loaded, setLoaded] = useState<boolean>(false);
184
185
// wait until the account settings are loaded to show the banner
186
useAsyncEffect(async () => {
187
const store = redux.getStore("account");
188
if (!store.get("is_ready")) {
189
await once(store, "is_ready");
190
}
191
setLoaded(true);
192
}, []);
193
194
const emailSendingEnabled = useTypedRedux("customize", "email_enabled");
195
196
const created = useTypedRedux("account", "created");
197
198
const dismissedTS = LS.get<number>(DISMISSED_KEY_LS);
199
200
const show_verify_email =
201
!email_address || !email_address_verified?.get(email_address);
202
203
// we also do not show this for newly created accounts
204
const now = getNow();
205
const oneDay = 1 * 24 * 60 * 60 * 1000;
206
const notTooNew = created != null && now > created.getTime() + oneDay;
207
208
// dismissed banner works for a week
209
const dismissed =
210
typeof dismissedTS === "number" && now < dismissedTS + 7 * oneDay;
211
212
return (
213
show_verify_email &&
214
loaded &&
215
notTooNew &&
216
!dismissed &&
217
emailSendingEnabled
218
);
219
}
220
221