Path: blob/master/src/packages/frontend/app/verify-email-banner.tsx
1496 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Modal, Space } from "antd";6import { FormattedMessage, useIntl } from "react-intl";78import { emailVerificationMsg } from "@cocalc/frontend/account/settings/email-verification";9import { Button } from "@cocalc/frontend/antd-bootstrap";10import {11redux,12useActions,13useAsyncEffect,14useState,15useTypedRedux,16} from "@cocalc/frontend/app-framework";17import { getNow } from "@cocalc/frontend/app/util";18import { Icon, Paragraph, Text } from "@cocalc/frontend/components";19import { labels } from "@cocalc/frontend/i18n";20import * as LS from "@cocalc/frontend/misc/local-storage-typed";21import { webapp_client } from "@cocalc/frontend/webapp-client";22import { once } from "@cocalc/util/async-utils";2324const DISMISSED_KEY_LS = "verify-email-dismissed";2526export function VerifyEmail() {27const [hide, setHide] = useState<boolean>(false);28const show = useShowVerifyEmail();2930function doDismiss(save = true) {31if (save) {32const now = getNow();33LS.set(DISMISSED_KEY_LS, now);34}35setHide(true);36}3738if (show && !hide) {39return <VerifyEmailModal doDismiss={doDismiss} />;40} else {41return null;42}43}4445function VerifyEmailModal({46doDismiss,47}: {48doDismiss: (save?: boolean) => void;49}) {50const intl = useIntl();51const page_actions = useActions("page");52const email_address = useTypedRedux("account", "email_address");5354const [error, setError] = useState<string>("");55const [sending, setSending] = useState<boolean>(false);56const [sent, setSent] = useState<boolean>(false);5758async function verify(): Promise<void> {59try {60setSending(true);61await webapp_client.account_client.send_verification_email();62} catch (err) {63const errMsg = `Problem sending email verification: ${err}`;64setError(errMsg);65} finally {66setSent(true);67}68}6970// TODO: at one point this should be a popup to just edit the email address71function edit() {72doDismiss(false);73page_actions.set_active_tab("account");74}7576function renderBanner() {77if (error) {78return <Text type="danger">{error}</Text>;79}80return (81<>82<Paragraph strong>83<FormattedMessage84id="app.verify-email-banner.text"85defaultMessage={`{sent, select,86true {Email Sent! Please check your email inbox (and maybe spam) and click on the confirmation link.}87other {Please check and verify your email address:}}`}88values={{89sent,90code: (c) => <Text code>{c}</Text>,91}}92/>93</Paragraph>94{!sent ? (95<>96<Paragraph code style={{ textAlign: "center" }}>97{email_address}98</Paragraph>99<Paragraph type="secondary">100<FormattedMessage101id="app.verify-email-banner.help.text"102defaultMessage="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."103/>104</Paragraph>105<Paragraph type="secondary">106<FormattedMessage107id="app.verify-email-banner.edit"108defaultMessage="If the email address is wrong, please <E>edit</E> it in the account settings."109values={{110E: (text) => (111<Button onClick={edit} bsSize="xsmall">112<Icon name="pencil" /> {text}113</Button>114),115}}116/>117</Paragraph>118</>119) : null}120</>121);122}123124function renderFooter() {125if (sent) {126return (127<Button onClick={() => doDismiss()} bsStyle="success">128{intl.formatMessage(labels.close)}129</Button>130);131}132133return (134<Space>135<Button onClick={() => doDismiss()}>136{intl.formatMessage(labels.close)}137</Button>138<Button139onClick={verify}140bsStyle="success"141active={!sent && sending}142disabled={sent || sending}143>144{intl.formatMessage(emailVerificationMsg, {145disabled_button: sent,146})}147</Button>148</Space>149);150}151152function renderTitle() {153return (154<>155<Icon name="mail" />{" "}156{intl.formatMessage({157id: "app.verify-email-banner.title",158defaultMessage: "Verify Your Email Address",159})}160</>161);162}163164return (165<Modal166title={renderTitle()}167open={true}168onCancel={() => doDismiss()}169footer={renderFooter()}170>171{renderBanner()}172</Modal>173);174}175176export function useShowVerifyEmail(): boolean {177const email_address = useTypedRedux("account", "email_address");178const email_address_verified = useTypedRedux(179"account",180"email_address_verified",181);182const [loaded, setLoaded] = useState<boolean>(false);183184// wait until the account settings are loaded to show the banner185useAsyncEffect(async () => {186const store = redux.getStore("account");187if (!store.get("is_ready")) {188await once(store, "is_ready");189}190setLoaded(true);191}, []);192193const emailSendingEnabled = useTypedRedux("customize", "email_enabled");194195const created = useTypedRedux("account", "created");196197const dismissedTS = LS.get<number>(DISMISSED_KEY_LS);198199const show_verify_email =200!email_address || !email_address_verified?.get(email_address);201202// we also do not show this for newly created accounts203const now = getNow();204const oneDay = 1 * 24 * 60 * 60 * 1000;205const notTooNew = created != null && now > created.getTime() + oneDay;206207// dismissed banner works for a week208const dismissed =209typeof dismissedTS === "number" && now < dismissedTS + 7 * oneDay;210211return (212show_verify_email &&213loaded &&214notTooNew &&215!dismissed &&216emailSendingEnabled217);218}219220221