Skip to content

Commit f309e1d

Browse files
committed
frontend/settings: refactor i18n, tweak terminal color scheme and add cocalc themes, move "communication" to its own category
1 parent fc86483 commit f309e1d

File tree

9 files changed

+298
-189
lines changed

9 files changed

+298
-189
lines changed

src/packages/frontend/account/account-page.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ import {
5050
AccountPreferencesAppearance,
5151
APPEARANCE_ICON_NAME,
5252
} from "./account-preferences-appearance";
53+
import {
54+
AccountPreferencesCommunication,
55+
COMMUNICATION_ICON_NAME,
56+
} from "./account-preferences-communication";
5357
import {
5458
AccountPreferencesEditor,
5559
EDITOR_ICON_NAME,
@@ -244,6 +248,19 @@ export const AccountPage: React.FC = () => {
244248
children: active_page === "preferences" &&
245249
active_sub_tab === "preferences-ai" && <AccountPreferencesAI />,
246250
},
251+
{
252+
key: "preferences-communication",
253+
label: (
254+
<span>
255+
<Icon name={COMMUNICATION_ICON_NAME} />{" "}
256+
{intl.formatMessage(labels.communication)}
257+
</span>
258+
),
259+
children: active_page === "preferences" &&
260+
active_sub_tab === "preferences-communication" && (
261+
<AccountPreferencesCommunication />
262+
),
263+
},
247264
{
248265
key: "preferences-keys",
249266
label: (
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* This file is part of CoCalc: Copyright © 2025 Sagemath, Inc.
3+
* License: MS-RSL – see LICENSE.md for details
4+
*/
5+
6+
import { Button } from "antd";
7+
import { FormattedMessage, useIntl } from "react-intl";
8+
9+
import { Panel, Switch } from "@cocalc/frontend/antd-bootstrap";
10+
import { redux, useTypedRedux } from "@cocalc/frontend/app-framework";
11+
import { Icon, IconName } from "@cocalc/frontend/components";
12+
import { labels } from "@cocalc/frontend/i18n";
13+
import { webapp_client } from "@cocalc/frontend/webapp-client";
14+
15+
export const COMMUNICATION_ICON_NAME: IconName = "mail";
16+
17+
export function AccountPreferencesCommunication(): React.JSX.Element {
18+
const intl = useIntl();
19+
const other_settings = useTypedRedux("account", "other_settings");
20+
const stripe_customer = useTypedRedux("account", "stripe_customer");
21+
const email_address_verified = useTypedRedux(
22+
"account",
23+
"email_address_verified",
24+
);
25+
const email_address = useTypedRedux("account", "email_address");
26+
const isVerified = !!email_address_verified?.get(email_address ?? "");
27+
const is_stripe_customer = !!stripe_customer?.getIn([
28+
"subscriptions",
29+
"total_count",
30+
]);
31+
32+
function on_change(name: string, value: any): void {
33+
redux.getActions("account").set_other_settings(name, value);
34+
}
35+
36+
function toggle_global_banner(val: boolean): void {
37+
if (val) {
38+
// this must be "null", not "undefined" – otherwise the data isn't stored in the DB.
39+
on_change("show_global_info2", null);
40+
} else {
41+
on_change("show_global_info2", webapp_client.server_time());
42+
}
43+
}
44+
45+
function render_global_banner() {
46+
return (
47+
<Switch
48+
checked={!other_settings.get("show_global_info2")}
49+
onChange={(e) => toggle_global_banner(e.target.checked)}
50+
>
51+
<FormattedMessage
52+
id="account.other-settings.global_banner"
53+
defaultMessage={`<strong>Show Announcement Banner</strong>: only shows up if there is a
54+
message`}
55+
/>
56+
</Switch>
57+
);
58+
}
59+
60+
function render_no_free_warnings() {
61+
const extra = is_stripe_customer ? (
62+
<span>(thanks for being a customer)</span>
63+
) : (
64+
<span>(only available to customers)</span>
65+
);
66+
67+
return (
68+
<Switch
69+
disabled={!is_stripe_customer}
70+
checked={!!other_settings.get("no_free_warnings")}
71+
onChange={(e) => on_change("no_free_warnings", e.target.checked)}
72+
>
73+
<strong>Hide free warnings</strong>: do{" "}
74+
<strong>
75+
<i>not</i>
76+
</strong>{" "}
77+
show a warning banner when using a free trial project {extra}
78+
</Switch>
79+
);
80+
}
81+
82+
function render_no_email_new_messages() {
83+
return (
84+
<>
85+
<Switch
86+
checked={other_settings.get("no_email_new_messages")}
87+
onChange={(e) => {
88+
on_change("no_email_new_messages", e.target.checked);
89+
}}
90+
>
91+
Do NOT send email when you get new{" "}
92+
<Button
93+
onClick={(e) => {
94+
e.stopPropagation();
95+
redux.getActions("page").set_active_tab("notifications");
96+
redux
97+
.getActions("mentions")
98+
.set_filter("messages-inbox" as "messages-inbox");
99+
}}
100+
type="link"
101+
size="small"
102+
>
103+
Internal Messages
104+
</Button>
105+
</Switch>
106+
{!isVerified && !other_settings.get("no_email_new_messages") && (
107+
<>
108+
(NOTE: You must also verify your email address above to get emails
109+
about new messages.)
110+
</>
111+
)}
112+
</>
113+
);
114+
}
115+
116+
return (
117+
<Panel
118+
size="small"
119+
header={
120+
<>
121+
<Icon name={COMMUNICATION_ICON_NAME} />{" "}
122+
{intl.formatMessage(labels.communication)}
123+
</>
124+
}
125+
>
126+
{render_global_banner()}
127+
{render_no_free_warnings()}
128+
{render_no_email_new_messages()}
129+
</Panel>
130+
);
131+
}

src/packages/frontend/account/other-settings.tsx

Lines changed: 5 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
// cSpell:ignore brandcolors codebar
77

8-
import { Button } from "antd";
98
import { FormattedMessage, useIntl } from "react-intl";
109

1110
import { Panel, Switch } from "@cocalc/frontend/antd-bootstrap";
@@ -19,7 +18,7 @@ import {
1918
NumberInput,
2019
Paragraph,
2120
SelectorInput,
22-
Text
21+
Text,
2322
} from "@cocalc/frontend/components";
2423
import AIAvatar from "@cocalc/frontend/components/ai-avatar";
2524
import { IS_MOBILE, IS_TOUCH } from "@cocalc/frontend/feature";
@@ -38,7 +37,6 @@ import {
3837
} from "@cocalc/frontend/project/page/activity-bar-consts";
3938
import { NewFilenameFamilies } from "@cocalc/frontend/project/utils";
4039
import track from "@cocalc/frontend/user-tracking";
41-
import { webapp_client } from "@cocalc/frontend/webapp-client";
4240
import { DEFAULT_NEW_FILENAMES, NEW_FILENAMES } from "@cocalc/util/db-schema";
4341
import { OTHER_SETTINGS_REPLY_ENGLISH_KEY } from "@cocalc/util/i18n/const";
4442

@@ -72,15 +70,6 @@ export function OtherSettings(props: Readonly<Props>): React.JSX.Element {
7270
redux.getActions("account").set_other_settings(name, value);
7371
}
7472

75-
function toggle_global_banner(val: boolean): void {
76-
if (val) {
77-
// this must be "null", not "undefined" – otherwise the data isn't stored in the DB.
78-
on_change("show_global_info2", null);
79-
} else {
80-
on_change("show_global_info2", webapp_client.server_time());
81-
}
82-
}
83-
8473
// private render_first_steps(): Rendered {
8574
// if (props.kucalc !== KUCALC_COCALC_COM) return;
8675
// return (
@@ -93,21 +82,6 @@ export function OtherSettings(props: Readonly<Props>): React.JSX.Element {
9382
// );
9483
// }
9584

96-
function render_global_banner(): Rendered {
97-
return (
98-
<Switch
99-
checked={!props.other_settings.get("show_global_info2")}
100-
onChange={(e) => toggle_global_banner(e.target.checked)}
101-
>
102-
<FormattedMessage
103-
id="account.other-settings.global_banner"
104-
defaultMessage={`<strong>Show Announcement Banner</strong>: only shows up if there is a
105-
message`}
106-
/>
107-
</Switch>
108-
);
109-
}
110-
11185
function render_confirm(): Rendered {
11286
if (!IS_MOBILE) {
11387
return (
@@ -237,71 +211,6 @@ export function OtherSettings(props: Readonly<Props>): React.JSX.Element {
237211
);
238212
}
239213

240-
function render_no_free_warnings(): Rendered {
241-
const isCustomer = props.is_stripe_customer;
242-
243-
const extra = isCustomer ? (
244-
<span>(thanks for being a customer)</span>
245-
) : (
246-
<span>(only available to customers)</span>
247-
);
248-
249-
return (
250-
<Switch
251-
disabled={!isCustomer}
252-
checked={!!props.other_settings.get("no_free_warnings")}
253-
onChange={(e) => on_change("no_free_warnings", e.target.checked)}
254-
>
255-
<strong>Hide free warnings</strong>: do{" "}
256-
<strong>
257-
<i>not</i>
258-
</strong>{" "}
259-
show a warning banner when using a free trial project {extra}
260-
</Switch>
261-
);
262-
}
263-
264-
function render_no_email_new_messages(): Rendered {
265-
const email_address_verified = useTypedRedux(
266-
"account",
267-
"email_address_verified",
268-
);
269-
const email_address = useTypedRedux("account", "email_address");
270-
const isVerified = !!email_address_verified?.get(email_address ?? "");
271-
272-
return (
273-
<>
274-
<Switch
275-
checked={props.other_settings.get("no_email_new_messages")}
276-
onChange={(e) => {
277-
on_change("no_email_new_messages", e.target.checked);
278-
}}
279-
>
280-
Do NOT send email when you get new{" "}
281-
<Button
282-
onClick={(e) => {
283-
e.stopPropagation();
284-
redux.getActions("page").set_active_tab("notifications");
285-
redux
286-
.getActions("mentions")
287-
.set_filter("messages-inbox" as "messages-inbox");
288-
}}
289-
type="link"
290-
size="small"
291-
>
292-
Internal Messages
293-
</Button>
294-
</Switch>
295-
{!isVerified && !props.other_settings.get("no_email_new_messages") && (
296-
<>
297-
(NOTE: You must also verify your email address above to get emails
298-
about new messages.)
299-
</>
300-
)}
301-
</>
302-
);
303-
}
304-
305214
function render_antd(): Rendered {
306215
return (
307216
<>
@@ -502,12 +411,7 @@ export function OtherSettings(props: Readonly<Props>): React.JSX.Element {
502411
size="small"
503412
header={
504413
<>
505-
<Icon name={THEME_ICON_NAME} />{" "}
506-
<FormattedMessage
507-
id="account.other-settings.theme"
508-
defaultMessage="Theme"
509-
description="Visual UI theme of the application"
510-
/>
414+
<Icon name={THEME_ICON_NAME} /> {intl.formatMessage(labels.theme)}
511415
</>
512416
}
513417
>
@@ -523,11 +427,7 @@ export function OtherSettings(props: Readonly<Props>): React.JSX.Element {
523427
size="small"
524428
header={
525429
<>
526-
<Icon name="desktop" />{" "}
527-
<FormattedMessage
528-
id="account.other-settings.browser_performance.title"
529-
defaultMessage="Browser"
530-
/>
430+
<Icon name="desktop" /> {intl.formatMessage(labels.browser)}
531431
</>
532432
}
533433
>
@@ -540,10 +440,7 @@ export function OtherSettings(props: Readonly<Props>): React.JSX.Element {
540440
header={
541441
<>
542442
<Icon name="folder-open" />{" "}
543-
<FormattedMessage
544-
id="account.other-settings.file_explorer.title"
545-
defaultMessage="File Explorer"
546-
/>
443+
{intl.formatMessage(labels.file_explorer)}
547444
</>
548445
}
549446
>
@@ -558,31 +455,13 @@ export function OtherSettings(props: Readonly<Props>): React.JSX.Element {
558455
size="small"
559456
header={
560457
<>
561-
<Icon name="edit" />{" "}
562-
<FormattedMessage
563-
id="account.other-settings.projects.title"
564-
defaultMessage="Projects"
565-
/>
458+
<Icon name="edit" /> {intl.formatMessage(labels.projects)}
566459
</>
567460
}
568461
>
569462
{render_vertical_fixed_bar_options()}
570463
</Panel>
571464

572-
<Panel
573-
size="small"
574-
header={
575-
<>
576-
<Icon name="mail" />{" "}
577-
<FormattedMessage {...labels.communication} />
578-
</>
579-
}
580-
>
581-
{render_global_banner()}
582-
{render_no_free_warnings()}
583-
{render_no_email_new_messages()}
584-
</Panel>
585-
586465
{/* Tours at bottom */}
587466
<Tours />
588467
</>

0 commit comments

Comments
 (0)