Skip to content

Commit 006eb47

Browse files
authored
Merge pull request #8401 from sagemathinc/banner-to-modals
frontend: convert some banners to dismissable modals
2 parents c84f285 + 3ed211f commit 006eb47

30 files changed

+613
-455
lines changed

src/packages/frontend/app/page.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ everything on *desktop*, once the user has signed in.
1111
declare var DEBUG: boolean;
1212

1313
import { Spin } from "antd";
14+
import { useIntl } from "react-intl";
15+
1416
import { Avatar } from "@cocalc/frontend/account/avatar/avatar";
1517
import { alert_message } from "@cocalc/frontend/alerts";
1618
import { Button } from "@cocalc/frontend/antd-bootstrap";
@@ -22,15 +24,17 @@ import {
2224
useState,
2325
useTypedRedux,
2426
} from "@cocalc/frontend/app-framework";
25-
import { IconName, Icon } from "@cocalc/frontend/components/icon";
27+
import { ClientContext } from "@cocalc/frontend/client/context";
28+
import { Icon, IconName } from "@cocalc/frontend/components/icon";
29+
import Next from "@cocalc/frontend/components/next";
2630
import { FileUsePage } from "@cocalc/frontend/file-use/page";
2731
import { labels } from "@cocalc/frontend/i18n";
2832
import { ProjectsNav } from "@cocalc/frontend/projects/projects-nav";
2933
import BalanceButton from "@cocalc/frontend/purchases/balance-button";
3034
import PayAsYouGoModal from "@cocalc/frontend/purchases/pay-as-you-go/modal";
3135
import openSupportTab from "@cocalc/frontend/support/open";
36+
import { webapp_client } from "@cocalc/frontend/webapp-client";
3237
import { COLORS } from "@cocalc/util/theme";
33-
import { useIntl } from "react-intl";
3438
import { IS_IOS, IS_MOBILE, IS_SAFARI } from "../feature";
3539
import { ActiveContent } from "./active-content";
3640
import { ConnectionIndicator } from "./connection-indicator";
@@ -45,12 +49,9 @@ import { Notification } from "./notifications";
4549
import PopconfirmModal from "./popconfirm-modal";
4650
import SettingsModal from "./settings-modal";
4751
import { HIDE_LABEL_THRESHOLD, NAV_CLASS } from "./top-nav-consts";
48-
import { useShowVerifyEmail, VerifyEmail } from "./verify-email-banner";
49-
import { CookieWarning, LocalStorageWarning } from "./warnings";
52+
import { VerifyEmail } from "./verify-email-banner";
5053
import VersionWarning from "./version-warning";
51-
import Next from "@cocalc/frontend/components/next";
52-
import { ClientContext } from "@cocalc/frontend/client/context";
53-
import { webapp_client } from "@cocalc/frontend/webapp-client";
54+
import { CookieWarning, LocalStorageWarning } from "./warnings";
5455

5556
// ipad and ios have a weird trick where they make the screen
5657
// actually smaller than 100vh and have it be scrollable, even
@@ -117,7 +118,6 @@ export const Page: React.FC = () => {
117118
const is_anonymous = useTypedRedux("account", "is_anonymous");
118119
const when_account_created = useTypedRedux("account", "created");
119120
const groups = useTypedRedux("account", "groups");
120-
const show_verify_email: boolean = useShowVerifyEmail();
121121
const show_i18n = useShowI18NBanner();
122122

123123
const is_commercial = useTypedRedux("customize", "is_commercial");
@@ -375,7 +375,7 @@ export const Page: React.FC = () => {
375375
{cookie_warning && <CookieWarning />}
376376
{local_storage_warning && <LocalStorageWarning />}
377377
{show_i18n && <I18NBanner />}
378-
{show_verify_email && <VerifyEmail />}
378+
<VerifyEmail />
379379
{!fullscreen && (
380380
<nav className="smc-top-bar" style={topBarStyle}>
381381
<AppLogo size={pageStyle.height} />

src/packages/frontend/app/util.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
import { webapp_client } from "@cocalc/frontend/webapp-client";
2+
13
export function blur_active_element(): void {
24
if (document.activeElement == null) return;
35
// otherwise, it'll be highlighted even when closed again
46
(document.activeElement as any).blur?.();
57
}
8+
9+
export function getNow(): number {
10+
return webapp_client.server_time().getTime();
11+
}

src/packages/frontend/app/verify-email-banner.tsx

Lines changed: 111 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,56 @@
33
* License: MS-RSL – see LICENSE.md for details
44
*/
55

6-
import { Space } from "antd";
6+
import { Modal, Space } from "antd";
77
import { FormattedMessage, useIntl } from "react-intl";
88

99
import { emailVerificationMsg } from "@cocalc/frontend/account/settings/email-verification";
1010
import { Button } from "@cocalc/frontend/antd-bootstrap";
1111
import {
12-
CSS,
1312
redux,
1413
useActions,
1514
useAsyncEffect,
1615
useState,
1716
useTypedRedux,
1817
} from "@cocalc/frontend/app-framework";
19-
import { CloseX2, HelpIcon, Icon, Text } from "@cocalc/frontend/components";
18+
import { getNow } from "@cocalc/frontend/app/util";
19+
import { Icon, Paragraph, Text } from "@cocalc/frontend/components";
2020
import { labels } from "@cocalc/frontend/i18n";
2121
import * as LS from "@cocalc/frontend/misc/local-storage-typed";
2222
import { webapp_client } from "@cocalc/frontend/webapp-client";
2323
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.ANTD_BG_RED_L,
31-
} as const;
3224

3325
const DISMISSED_KEY_LS = "verify-email-dismissed";
3426

3527
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+
}) {
3651
const intl = useIntl();
3752
const page_actions = useActions("page");
3853
const email_address = useTypedRedux("account", "email_address");
3954

4055
const [error, setError] = useState<string>("");
41-
const [show, setShow] = useState<boolean>(true);
4256
const [sending, setSending] = useState<boolean>(false);
4357
const [sent, setSent] = useState<boolean>(false);
4458

@@ -56,80 +70,107 @@ export function VerifyEmail() {
5670

5771
// TODO: at one point this should be a popup to just edit the email address
5872
function edit() {
73+
doDismiss(false);
5974
page_actions.set_active_tab("account");
6075
}
6176

62-
function dismiss() {
63-
const now = webapp_client.server_time().getTime();
64-
LS.set(DISMISSED_KEY_LS, now);
65-
setShow(false);
66-
}
67-
6877
function renderBanner() {
6978
if (error) {
7079
return <Text type="danger">{error}</Text>;
7180
}
7281
return (
73-
<Text strong>
74-
<Icon name="mail" />{" "}
75-
<FormattedMessage
76-
id="app.verify-email-banner.text"
77-
defaultMessage={`{sent, select,
78-
true {Sent! Please check your email inbox (and 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-
>
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">
115101
<FormattedMessage
116102
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."
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."
118104
/>
119-
</HelpIcon>
120-
</Space>
121-
)}
122-
</Text>
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+
</>
123122
);
124123
}
125124

126-
if (!show) return;
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+
}
127164

128165
return (
129-
<div style={VERIFY_EMAIL_STYLE}>
166+
<Modal
167+
title={renderTitle()}
168+
open={true}
169+
onCancel={() => doDismiss()}
170+
footer={renderFooter()}
171+
>
130172
{renderBanner()}
131-
<CloseX2 close={dismiss} />
132-
</div>
173+
</Modal>
133174
);
134175
}
135176

@@ -160,7 +201,7 @@ export function useShowVerifyEmail(): boolean {
160201
!email_address || !email_address_verified?.get(email_address);
161202

162203
// we also do not show this for newly created accounts
163-
const now = webapp_client.server_time().getTime();
204+
const now = getNow();
164205
const oneDay = 1 * 24 * 60 * 60 * 1000;
165206
const notTooNew = created != null && now > created.getTime() + oneDay;
166207

src/packages/frontend/i18n/common.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,11 @@ export const labels = defineMessages({
514514
id: "labels.close",
515515
defaultMessage: "Close",
516516
},
517+
dismiss: {
518+
id: "labels.dismiss",
519+
defaultMessage: "Dismiss",
520+
description: "Short label on a button to dismiss a dialog or modal",
521+
},
517522
guide: {
518523
id: "labels.guide",
519524
defaultMessage: "Guide",

src/packages/frontend/i18n/trans/ar_EG.json

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"account.other-settings.filename_generator.description": "اختر كيفية توليد أسماء الملفات التلقائية. بشكل خاص، لجعلها فريدة أو لتضمين الوقت الحالي.",
7272
"account.other-settings.filename_generator.label": "مولد اسم الملف",
7373
"account.other-settings.global_banner": "<strong>عرض لافتة الإعلان</strong>: يظهر فقط إذا كان هناك رسالة",
74+
"account.other-settings.hide_navbar_balance": "<strong>إخفاء رصيد الحساب</strong> في شريط التنقل",
7475
"account.other-settings.katex": "<strong>KaTeX:</strong> محاولة عرض الصيغ باستخدام {katex} (أسرع بكثير، ولكن بدون خيارات قائمة السياق)",
7576
"account.other-settings.llm.default_llm": "نموذج اللغة الافتراضي AI",
7677
"account.other-settings.llm.disable_all": "<strong>تعطيل جميع تكاملات الذكاء الاصطناعي</strong>، مثل أزرار توليد أو شرح الأكواد في Jupyter، وذكر @chatgpt، إلخ.",
@@ -128,9 +129,10 @@
128129
"ai-generate-document.modal.title": "إنشاء مستند {docName} باستخدام الذكاء الاصطناعي",
129130
"ai-generator.select_llm": "اختر نموذج اللغة",
130131
"app.fullscreen-button.tooltip": "وضع الشاشة الكاملة، يركز على المستند أو الصفحة الحالية.",
131-
"app.verify-email-banner.help.text": "من المهم أن يكون لديك عنوان بريد إلكتروني صالح. نستخدمه لإعادة تعيين كلمة المرور، إرسال الرسائل، إشعارات الفواتير، والدعم. يرجى التأكد من صحة بريدك الإلكتروني للبقاء على اطلاع.",
132-
"app.verify-email-banner.help.title": "التحقق من البريد الإلكتروني",
133-
"app.verify-email-banner.text": "{sent, select, true {تم الإرسال! يرجى التحقق من صندوق البريد الإلكتروني الخاص بك (ربما البريد العشوائي) والنقر على رابط التأكيد.} other {يرجى التحقق من عنوان بريدك الإلكتروني وتأكيده: <code>{email}</code>}}",
132+
"app.verify-email-banner.edit": "إذا كانت عنوان البريد الإلكتروني خاطئة، يرجى <E>تعديله</E> في إعدادات الحساب.",
133+
"app.verify-email-banner.help.text": "من المهم أن يكون لديك عنوان بريد إلكتروني يعمل. نستخدمه لإعادة تعيين كلمة المرور، وإرسال الرسائل، وإشعارات الفوترة، والدعم. يرجى التأكد من صحة بريدك الإلكتروني للبقاء على اطلاع.",
134+
"app.verify-email-banner.text": "{sent, select, true {تم إرسال البريد الإلكتروني! يرجى التحقق من صندوق البريد الإلكتروني (وربما الرسائل غير المرغوب فيها) والنقر على رابط التأكيد.} other {يرجى التحقق من عنوان بريدك الإلكتروني وتأكيده:}}",
135+
"app.verify-email-banner.title": "تحقق من عنوان بريدك الإلكتروني",
134136
"chat.chat-indicator.tooltip": "إخفاء أو إظهار محادثة المستندات",
135137
"chat.input.placeholder": "اكتب رسالة جديدة ({have_llm, select, true {الدردشة مع الذكاء الاصطناعي أو } other {}}إخطار زميل بكتابة @)...",
136138
"chatroom.chat_input.preview_button.label": "عرض",
@@ -305,10 +307,6 @@
305307
"commands.generic.toggle_button_bar.label": "{show, select, true {إخفاء} other {إظهار}} شريط الأدوات...",
306308
"commands.generic.toggle_button_bar.title": "تبديل عرض أو إخفاء شريط أدوات القائمة لجميع المحررين.",
307309
"components.test-i18n.msg": "<p>منسق مع <b>عريض</b> و<i>مائل</i>.</p>",
308-
"connection-info.hub_server": "خادم المحور",
309-
"connection-info.info": "يتغير لون أيقونة الاتصال مع زيادة عدد الرسائل المرسلة إلى المحور. عادةً، لا يلزم اتخاذ أي إجراء، لكن الأعداد مفيدة لأغراض التشخيص. الحد الأقصى لعدد الرسائل التي يمكن إرسالها في نفس الوقت هو {max}.",
310-
"connection-info.messages_received": "استلم",
311-
"connection-info.messages_sent": "أُرْسِل",
312310
"connection-info.ping": "وقت الاستجابة",
313311
"connection-info.ping_info": "{avgping}مللي ثانية (الأحدث: {ping}مللي ثانية)",
314312
"course.actions": "إجراءات",
@@ -909,6 +907,7 @@
909907
"labels.deleted": "محذوف",
910908
"labels.description": "الوصف",
911909
"labels.disconnected": "غير متصل",
910+
"labels.dismiss": "رفض",
912911
"labels.documentation": "التوثيق",
913912
"labels.download": "تنزيل",
914913
"labels.downloaded": "تم التنزيل",
@@ -1217,6 +1216,10 @@
12171216
"project.new.new-file-page.create.tooltip": "{title}. يمكنك أيضًا الضغط على العودة",
12181217
"project.new.new-file-page.upload.description": "يمكنك إسقاط ملف أو أكثر هنا أو في قائمة ملفات المستكشف. اطلع على <A>التوثيق</A> لمزيد من الطرق لإضافة ملفاتك إلى مشروعك.",
12191218
"project.new.new-file-page.upload.title": "تحميل الملفات إلى مشروعك",
1219+
"project.no-internet-modal.add-license": "إضافة الترخيص",
1220+
"project.no-internet-modal.info": "<strong>تم تعطيل الوصول إلى الإنترنت لهذا المشروع.</strong> هذا التقييد يمنع تثبيت حزم بايثون (pip، conda) أو حزم R، واستخدام Git لاستنساخ المستودعات، وتنزيل مجموعات البيانات أو الوصول إلى واجهات برمجة التطبيقات. قد تتعطل الأوامر التي تحاول الوصول إلى الإنترنت أو تفشل في الاكتمال، حيث يتم حظر الاتصالات الشبكية لمنع سوء الاستخدام. <A>تعرف على المزيد</A>.",
1221+
"project.no-internet-modal.message": "لحل هذه المشكلة، <A1>تحتاج إلى تطبيق</A1> <A2>ترخيص صالح</A2> يوفر ترقيات أو <A3>شراء ترخيص</A3>.",
1222+
"project.no-internet-modal.title": "لا يوجد اتصال بالإنترنت",
12201223
"project.open_file.what": "افتح الملف \"{path}\"",
12211224
"project.page.ai-generate-document.content.label": "قدم وصفًا مفصلًا لوثيقة {docName} التي تريد إنشاؤها:",
12221225
"project.page.ai-generate-document.content.placeholder": "وصف المحتوى...",

0 commit comments

Comments
 (0)