|
3 | 3 | * License: MS-RSL – see LICENSE.md for details |
4 | 4 | */ |
5 | 5 |
|
6 | | -import { Card, InputNumber } from "antd"; |
| 6 | +import { Button, Card, Slider } from "antd"; |
| 7 | +import { debounce } from "lodash"; |
7 | 8 | import { Map } from "immutable"; |
8 | | -import { FormattedMessage, useIntl } from "react-intl"; |
| 9 | +import { useMemo } from "react"; |
| 10 | +import { defineMessages, FormattedMessage, useIntl } from "react-intl"; |
9 | 11 |
|
10 | 12 | import { Checkbox, Panel } from "@cocalc/frontend/antd-bootstrap"; |
11 | 13 | import { Rendered, redux, useTypedRedux } from "@cocalc/frontend/app-framework"; |
@@ -41,13 +43,34 @@ import track from "@cocalc/frontend/user-tracking"; |
41 | 43 | import { webapp_client } from "@cocalc/frontend/webapp-client"; |
42 | 44 | import { DEFAULT_NEW_FILENAMES, NEW_FILENAMES } from "@cocalc/util/db-schema"; |
43 | 45 | import { OTHER_SETTINGS_REPLY_ENGLISH_KEY } from "@cocalc/util/i18n/const"; |
44 | | -import { dark_mode_mins, get_dark_mode_config } from "./dark-mode"; |
| 46 | +import { |
| 47 | + DARK_MODE_ICON, |
| 48 | + DARK_MODE_KEYS, |
| 49 | + DARK_MODE_MINS, |
| 50 | + get_dark_mode_config, |
| 51 | +} from "./dark-mode"; |
| 52 | +import { DARK_MODE_DEFAULTS } from "@cocalc/util/db-schema/accounts"; |
45 | 53 | import { I18NSelector, I18N_MESSAGE, I18N_TITLE } from "./i18n-selector"; |
46 | 54 | import Messages from "./messages"; |
47 | 55 | import Tours from "./tours"; |
48 | 56 | import { useLanguageModelSetting } from "./useLanguageModelSetting"; |
49 | 57 | import { UserDefinedLLMComponent } from "./user-defined-llm"; |
50 | 58 |
|
| 59 | +const DARK_MODE_LABELS = defineMessages({ |
| 60 | + brightness: { |
| 61 | + id: "account.other-settings.theme.dark_mode.brightness", |
| 62 | + defaultMessage: "Brightness", |
| 63 | + }, |
| 64 | + contrast: { |
| 65 | + id: "account.other-settings.theme.dark_mode.contrast", |
| 66 | + defaultMessage: "Contrast", |
| 67 | + }, |
| 68 | + sepia: { |
| 69 | + id: "account.other-settings.theme.dark_mode.sepia", |
| 70 | + defaultMessage: "Sepia", |
| 71 | + }, |
| 72 | +}); |
| 73 | + |
51 | 74 | // See https://github.com/sagemathinc/cocalc/issues/5620 |
52 | 75 | // There are weird bugs with relying only on mathjax, whereas our |
53 | 76 | // implementation of katex with a fallback to mathjax works very well. |
@@ -79,6 +102,16 @@ export function OtherSettings(props: Readonly<Props>): React.JSX.Element { |
79 | 102 | redux.getActions("account").set_other_settings(name, value); |
80 | 103 | } |
81 | 104 |
|
| 105 | + // Debounced version for dark mode sliders to reduce CPU usage |
| 106 | + const on_change_dark_mode = useMemo( |
| 107 | + () => |
| 108 | + debounce((name: string, value: any) => on_change(name, value), 50, { |
| 109 | + trailing: true, |
| 110 | + leading: false, |
| 111 | + }), |
| 112 | + [], |
| 113 | + ); |
| 114 | + |
82 | 115 | function toggle_global_banner(val: boolean): void { |
83 | 116 | if (val) { |
84 | 117 | // this must be "null", not "undefined" – otherwise the data isn't stored in the DB. |
@@ -363,7 +396,6 @@ export function OtherSettings(props: Readonly<Props>): React.JSX.Element { |
363 | 396 | function render_dark_mode(): Rendered { |
364 | 397 | const checked = !!props.other_settings.get("dark_mode"); |
365 | 398 | const config = get_dark_mode_config(props.other_settings.toJS()); |
366 | | - const label_style = { width: "100px", display: "inline-block" } as const; |
367 | 399 | return ( |
368 | 400 | <div> |
369 | 401 | <Checkbox |
@@ -395,62 +427,51 @@ export function OtherSettings(props: Readonly<Props>): React.JSX.Element { |
395 | 427 | {checked ? ( |
396 | 428 | <Card |
397 | 429 | size="small" |
398 | | - title={intl.formatMessage({ |
399 | | - id: "account.other-settings.theme.dark_mode.configuration", |
400 | | - defaultMessage: "Dark Mode Configuration", |
401 | | - })} |
| 430 | + title={ |
| 431 | + <> |
| 432 | + <Icon unicode={DARK_MODE_ICON} />{" "} |
| 433 | + {intl.formatMessage({ |
| 434 | + id: "account.other-settings.theme.dark_mode.configuration", |
| 435 | + defaultMessage: "Dark Mode Configuration", |
| 436 | + })} |
| 437 | + </> |
| 438 | + } |
402 | 439 | > |
403 | | - <span style={label_style}> |
404 | | - <FormattedMessage |
405 | | - id="account.other-settings.theme.dark_mode.brightness" |
406 | | - defaultMessage="Brightness" |
407 | | - /> |
408 | | - </span> |
409 | | - <InputNumber |
410 | | - min={dark_mode_mins.brightness} |
411 | | - max={100} |
412 | | - value={config.brightness} |
413 | | - onChange={(x) => on_change("dark_mode_brightness", x)} |
414 | | - /> |
415 | | - <br /> |
416 | | - <span style={label_style}> |
417 | | - <FormattedMessage |
418 | | - id="account.other-settings.theme.dark_mode.contrast" |
419 | | - defaultMessage="Contrast" |
420 | | - /> |
421 | | - </span> |
422 | | - <InputNumber |
423 | | - min={dark_mode_mins.contrast} |
424 | | - max={100} |
425 | | - value={config.contrast} |
426 | | - onChange={(x) => on_change("dark_mode_contrast", x)} |
427 | | - /> |
428 | | - <br /> |
429 | | - <span style={label_style}> |
430 | | - <FormattedMessage |
431 | | - id="account.other-settings.theme.dark_mode.sepia" |
432 | | - defaultMessage="Sepia" |
433 | | - /> |
434 | | - </span> |
435 | | - <InputNumber |
436 | | - min={dark_mode_mins.sepia} |
437 | | - max={100} |
438 | | - value={config.sepia} |
439 | | - onChange={(x) => on_change("dark_mode_sepia", x)} |
440 | | - /> |
441 | | - <br /> |
442 | | - <span style={label_style}> |
443 | | - <FormattedMessage |
444 | | - id="account.other-settings.theme.dark_mode.grayscale" |
445 | | - defaultMessage="Grayscale" |
446 | | - /> |
447 | | - </span> |
448 | | - <InputNumber |
449 | | - min={dark_mode_mins.grayscale} |
450 | | - max={100} |
451 | | - value={config.grayscale} |
452 | | - onChange={(x) => on_change("dark_mode_grayscale", x)} |
453 | | - /> |
| 440 | + <div style={{ display: "flex", flexDirection: "column", gap: 5 }}> |
| 441 | + {DARK_MODE_KEYS.map((key) => ( |
| 442 | + <div |
| 443 | + key={key} |
| 444 | + style={{ display: "flex", gap: 10, alignItems: "center" }} |
| 445 | + > |
| 446 | + <div style={{ width: 100 }}> |
| 447 | + {intl.formatMessage(DARK_MODE_LABELS[key])} |
| 448 | + </div> |
| 449 | + <Slider |
| 450 | + min={DARK_MODE_MINS[key]} |
| 451 | + max={100} |
| 452 | + value={config[key]} |
| 453 | + onChange={(x) => on_change_dark_mode(`dark_mode_${key}`, x)} |
| 454 | + marks={{ |
| 455 | + [DARK_MODE_DEFAULTS[key]]: String( |
| 456 | + DARK_MODE_DEFAULTS[key], |
| 457 | + ), |
| 458 | + }} |
| 459 | + style={{ flex: 1, width: 0 }} |
| 460 | + /> |
| 461 | + <Button |
| 462 | + size="small" |
| 463 | + onClick={() => |
| 464 | + on_change_dark_mode( |
| 465 | + `dark_mode_${key}`, |
| 466 | + DARK_MODE_DEFAULTS[key], |
| 467 | + ) |
| 468 | + } |
| 469 | + > |
| 470 | + {intl.formatMessage(labels.reset)} |
| 471 | + </Button> |
| 472 | + </div> |
| 473 | + ))} |
| 474 | + </div> |
454 | 475 | </Card> |
455 | 476 | ) : undefined} |
456 | 477 | </div> |
|
0 commit comments