CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/app/verify-email-banner.tsx
Views: 821
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 { 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
CSS,
13
redux,
14
useActions,
15
useAsyncEffect,
16
useState,
17
useTypedRedux,
18
} from "@cocalc/frontend/app-framework";
19
import { CloseX2, HelpIcon, Icon, 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
import { COLORS } from "@cocalc/util/theme";
25
26
const VERIFY_EMAIL_STYLE: CSS = {
27
width: "100%",
28
padding: "5px",
29
borderBottom: `1px solid ${COLORS.GRAY_D}`,
30
background: COLORS.ATND_BG_RED_L,
31
} as const;
32
33
const DISMISSED_KEY_LS = "verify-email-dismissed";
34
35
export function VerifyEmail() {
36
const intl = useIntl();
37
const page_actions = useActions("page");
38
const email_address = useTypedRedux("account", "email_address");
39
40
const [error, setError] = useState<string>("");
41
const [show, setShow] = useState<boolean>(true);
42
const [sending, setSending] = useState<boolean>(false);
43
const [sent, setSent] = useState<boolean>(false);
44
45
async function verify(): Promise<void> {
46
try {
47
setSending(true);
48
await webapp_client.account_client.send_verification_email();
49
} catch (err) {
50
const errMsg = `Problem sending email verification: ${err}`;
51
setError(errMsg);
52
} finally {
53
setSent(true);
54
}
55
}
56
57
// TODO: at one point this should be a popup to just edit the email address
58
function edit() {
59
page_actions.set_active_tab("account");
60
}
61
62
function dismiss() {
63
const now = webapp_client.server_time().getTime();
64
LS.set(DISMISSED_KEY_LS, now);
65
setShow(false);
66
}
67
68
function renderBanner() {
69
if (error) {
70
return <Text type="danger">{error}</Text>;
71
}
72
return (
73
<Text strong>
74
<Icon name="mail" />{" "}
75
<FormattedMessage
76
id="app.verify-email-banner.text"
77
defaultMessage={`{sent, select,
78
true {Sent! Plesae check your email inbox (maybe spam) and click on the confirmation link.}
79
other {Please check and verify your email address: <code>{email}</code>}}`}
80
values={{
81
sent,
82
email: email_address,
83
code: (c) => <Text code>{c}</Text>,
84
}}
85
/>{" "}
86
{sent ? (
87
<Button
88
onClick={() => setShow(false)}
89
bsStyle="success"
90
bsSize={"xsmall"}
91
>
92
{intl.formatMessage(labels.close)}
93
</Button>
94
) : (
95
<Space size={"small"}>
96
<Button bsSize={"xsmall"} onClick={edit}>
97
<Icon name="pencil" /> {intl.formatMessage(labels.edit)}
98
</Button>
99
<Button
100
onClick={verify}
101
bsStyle="success"
102
disabled={sent || sending}
103
bsSize={"xsmall"}
104
>
105
{intl.formatMessage(emailVerificationMsg, {
106
disabled_button: sent,
107
})}
108
</Button>
109
<HelpIcon
110
title={intl.formatMessage({
111
id: "app.verify-email-banner.help.title",
112
defaultMessage: "Email Verification",
113
})}
114
>
115
<FormattedMessage
116
id="app.verify-email-banner.help.text"
117
defaultMessage="It's important to have a working email address. We use this for password resets, sending messages, billing notifications, and support. Please ensure your email is correct to stay informed."
118
/>
119
</HelpIcon>
120
</Space>
121
)}
122
</Text>
123
);
124
}
125
126
if (!show) return;
127
128
return (
129
<div style={VERIFY_EMAIL_STYLE}>
130
{renderBanner()}
131
<CloseX2 close={dismiss} />
132
</div>
133
);
134
}
135
136
export function useShowVerifyEmail(): boolean {
137
const email_address = useTypedRedux("account", "email_address");
138
const email_address_verified = useTypedRedux(
139
"account",
140
"email_address_verified",
141
);
142
const [loaded, setLoaded] = useState<boolean>(false);
143
144
// wait until the account settings are loaded to show the banner
145
useAsyncEffect(async () => {
146
const store = redux.getStore("account");
147
if (!store.get("is_ready")) {
148
await once(store, "is_ready");
149
}
150
setLoaded(true);
151
}, []);
152
153
const created = useTypedRedux("account", "created");
154
155
const dismissedTS = LS.get<number>(DISMISSED_KEY_LS);
156
157
const show_verify_email =
158
!email_address || !email_address_verified?.get(email_address);
159
160
// we also do not show this for newly created accounts
161
const now = webapp_client.server_time().getTime();
162
const oneDay = 1 * 24 * 60 * 60 * 1000;
163
const notTooNew = created != null && now > created.getTime() + oneDay;
164
165
// dismissed banner works for a week
166
const dismissed =
167
typeof dismissedTS === "number" && now < dismissedTS + 7 * oneDay;
168
169
return show_verify_email && loaded && notTooNew && !dismissed;
170
}
171
172