diff --git a/src/packages/frontend/account/settings/account-settings.tsx b/src/packages/frontend/account/settings/account-settings.tsx index 98319edc02..a0a7fd6341 100644 --- a/src/packages/frontend/account/settings/account-settings.tsx +++ b/src/packages/frontend/account/settings/account-settings.tsx @@ -49,7 +49,6 @@ import { PassportStrategyFrontend } from "@cocalc/util/types/passport-types"; import { DeleteAccount } from "../delete-account"; import { SignOut } from "../sign-out"; import { set_account_table, ugly_error } from "../util"; -import { APIKeySetting } from "./api-key"; import { EmailAddressSetting } from "./email-address-setting"; import { EmailVerification } from "./email-verification"; import { PasswordSetting } from "./password-setting"; @@ -578,7 +577,6 @@ will no longer work (automatic redirects are not implemented), so change with ca } return ( ; - } - return ( {render_anonymous_warning()} @@ -634,7 +627,6 @@ will no longer work (automatic redirects are not implemented), so change with ca
{render_email_verification()} {render_password()} - {render_api_key()} {render_created()} {render_delete_account()} {render_linked_external_accounts()} diff --git a/src/packages/frontend/account/settings/api-key.tsx b/src/packages/frontend/account/settings/api-key.tsx deleted file mode 100644 index 50eab62946..0000000000 --- a/src/packages/frontend/account/settings/api-key.tsx +++ /dev/null @@ -1,321 +0,0 @@ -/* - * This file is part of CoCalc: Copyright © 2020 Sagemath, Inc. - * License: MS-RSL – see LICENSE.md for details - */ - -// API Key Configuration -import { Button, FormControl, Well } from "@cocalc/frontend/antd-bootstrap"; -import { Component, ReactDOM, Rendered } from "@cocalc/frontend/app-framework"; -import { - A, - CloseX, - CopyToClipBoard, - ErrorDisplay, - LabeledRow, - Loading, -} from "@cocalc/frontend/components"; -import { webapp_client } from "@cocalc/frontend/webapp-client"; -import { startswith } from "@cocalc/util/misc"; - -/* -The states are: - 'init' - initial state -- show nothing and wait to click to request to view key; no info - 'error' - showing an error - 'password' - requesting password from user - 'loading' - loading something from backend (doing api call) - 'showkey' - showing the api key (or that there is none) - 'confirm-get' - confirming getting API key (isn't used) - 'confirm-delete' - confirming delete of API key - 'confirm-regenerate' - confirming regenerate of API key -*/ - -type Action = "get" | "delete" | "regenerate"; -type State = - | "error" - | "init" - | "loading" - | "password" - | "showkey" - | "confirm-get" - | "confirm-delete" - | "confirm-regenerate"; - -interface ComponentState { - api_key?: string; // set, if it has been loaded - password: string; // must be defined so that input control is controlled - error?: string; - state: State; -} - -export class APIKeySetting extends Component<{}, ComponentState> { - private mounted: boolean = true; - - constructor(props, state) { - super(props, state); - this.state = { - password: "", - state: "init", - }; - } - - componentWillUnmount() { - this.mounted = false; - } - - private render_confirm(): Rendered { - let action: Action, mesg: string; - switch (this.state.state) { - case "confirm-delete": - action = "delete"; - mesg = "Are you sure you want to delete your API key? "; - break; - case "confirm-regenerate": - action = "regenerate"; - mesg = "Are you sure you want to regenerate your API key? "; - break; - default: - throw Error("bug -- invalid state for render_confirm"); - } - return ( -
- {mesg}{" "} - - Anything using the current API key will stop working. - -
-
- - -
- ); - } - - private async do_action(action: Action): Promise { - this.setState({ state: "loading" }); - try { - const api_key = await webapp_client.account_client.api_key( - action, - this.state.password - ); - if (this.mounted) { - this.setState({ api_key, state: "showkey" }); - } - } catch (err) { - if (this.mounted) { - this.setState({ - error: err, - password: "", - api_key: undefined, - state: "error", - }); - } - } - } - - private render_api_key(): Rendered { - if (this.state.api_key) { - return ( -
- - {this.render_button("delete", "Delete key")} -

- If at all possible, you should delete this key and use the new "Api - Keys" panel below instead. -

-
- ); - } else { - return
Please use the new "Api Keys" panel below instead.
; - } - } - - private click_action_button(action: Action): void { - switch (this.state.state) { - case "init": - this.setState({ state: "password" }); - return; - case "password": - this.do_action(action); - return; - case "showkey": - if (this.state.api_key) { - // Typescript isn't smart enough for this... - this.setState({ state: `confirm-${action}` as State }); - } else { - this.do_action(action); - } - return; - } - } - - private render_button( - action: Action, - name: string | undefined = undefined, - disabled: boolean = false - ): Rendered { - if (name == null) { - switch (action) { - case "get": - name = "Reveal Key"; - break; - case "delete": - name = "Delete Key"; - break; - case "regenerate": - name = "Regenerate Key"; - break; - } - } - if (startswith(this.state.state, "confirm-")) { - disabled = true; - } - return ( - - ); - } - - private render_get_password(): Rendered { - return ( -
- { - const password = ReactDOM.findDOMNode(this.refs.password)?.value; - if (password != null) { - this.setState({ - password, - }); - } - }} - /> - {this.render_button( - "get", - undefined, - !this.state.password && this.state.password.length > 6 - )} -
- ); - } - - private render_content(): Rendered { - if (this.state.error) { - return ( - - this.setState({ error: "", state: "init", password: "" }) - } - /> - ); - } - switch (this.state.state) { - case "loading": - return ; - case "password": - return this.render_get_password(); - case "showkey": - return this.render_api_key(); - case "confirm-delete": - case "confirm-regenerate": - return ( - - {this.render_api_key()} -
- {this.render_confirm()} -
- ); - } - } - - private render_close(): Rendered { - return ( - this.setState({ password: "", state: "init" })} - style={{ marginRight: "5px", marginLeft: "20px" }} - /> - ); - } - - private render_workaround(): Rendered { - if (this.state.state != "password") return; - return ( - <> -
- NOTE: If you do not have a password set, there is{" "} - - a workaround to generate your API key. - - - ); - } - - private render_docs(): Rendered { - return ( -
-
- - API documentation... - {this.render_workaround()} - -
- ); - } - - private render_well(): Rendered { - return ( - - {this.render_close()} - {this.render_content()} - {this.render_docs()} - - ); - } - - private render_init(): Rendered { - return
{this.render_button("get")}
; - } - - public render(): Rendered { - return ( - -
- {this.state.state === "init" - ? this.render_init() - : this.render_well()} -
-
- ); - } -} diff --git a/src/packages/frontend/account/settings/email-address-setting.tsx b/src/packages/frontend/account/settings/email-address-setting.tsx index 79726586f5..98c7003252 100644 --- a/src/packages/frontend/account/settings/email-address-setting.tsx +++ b/src/packages/frontend/account/settings/email-address-setting.tsx @@ -4,21 +4,9 @@ */ import { FormattedMessage, useIntl } from "react-intl"; - import { alert_message } from "@cocalc/frontend/alerts"; -import { - Button, - ButtonToolbar, - FormControl, - FormGroup, - Well, -} from "@cocalc/frontend/antd-bootstrap"; -import { - ReactDOM, - Rendered, - useRef, - useState, -} from "@cocalc/frontend/app-framework"; +import { Button, Card, Input, Space } from "antd"; +import { useState } from "react"; import { ErrorDisplay, LabeledRow, Saving } from "@cocalc/frontend/components"; import { labels } from "@cocalc/frontend/i18n"; import { log } from "@cocalc/frontend/user-tracking"; @@ -26,32 +14,34 @@ import { webapp_client } from "@cocalc/frontend/webapp-client"; import { COLORS } from "@cocalc/util/theme"; interface Props { - account_id: string; email_address?: string; disabled?: boolean; is_anonymous?: boolean; verify_emails?: boolean; } -export const EmailAddressSetting = (props: Readonly) => { +export const EmailAddressSetting = ({ + email_address: email_address0, + disabled, + is_anonymous, + verify_emails, +}: Props) => { const intl = useIntl(); - - const emailRef = useRef(null); - const passwordRef = useRef(null); - const [state, setState] = useState<"view" | "edit" | "saving">("view"); const [password, setPassword] = useState(""); - const [email_address, set_email_address] = useState(""); + const [email_address, set_email_address] = useState( + email_address0 ?? "", + ); const [error, setError] = useState(""); - function start_editing(): void { + function start_editing() { setState("edit"); - set_email_address(props.email_address != null ? props.email_address : ""); + set_email_address(email_address0 ?? ""); setError(""); setPassword(""); } - function cancel_editing(): void { + function cancel_editing() { setState("view"); setPassword(""); } @@ -70,7 +60,7 @@ export const EmailAddressSetting = (props: Readonly) => { setError(`Error -- ${error}`); return; } - if (props.is_anonymous) { + if (is_anonymous) { log("email_sign_up", { source: "anonymous_account" }); } setState("view"); @@ -79,14 +69,12 @@ export const EmailAddressSetting = (props: Readonly) => { // if email verification is enabled, send out a token // in any case, send a welcome email to an anonymous user, possibly // including an email verification link - if (!(props.verify_emails || props.is_anonymous)) { + if (!(verify_emails || is_anonymous)) { return; } try { // anonymouse users will get the "welcome" email - await webapp_client.account_client.send_verification_email( - !props.is_anonymous, - ); + await webapp_client.account_client.send_verification_email(!is_anonymous); } catch (error) { const err_msg = `Problem sending welcome email: ${error}`; console.log(err_msg); @@ -95,22 +83,10 @@ export const EmailAddressSetting = (props: Readonly) => { } function is_submittable(): boolean { - return !!(password !== "" && email_address !== props.email_address); + return !!(password !== "" && email_address !== email_address0); } - function render_change_button(): Rendered { - return ( - - ); - } - - function render_error(): Rendered { + function render_error() { if (error) { return ( ) => { } } - function render_edit(): Rendered { + function render_edit() { const password_label = intl.formatMessage( { id: "account.settings.email_address.password_label", @@ -130,64 +106,58 @@ export const EmailAddressSetting = (props: Readonly) => { "{have_email, select, true {Current password} other {Choose a password}}", }, { - have_email: !!props.email_address, + have_email: !!email_address, }, ); return ( - - + +
- { - const em = ReactDOM.findDOMNode(emailRef.current)?.value; - set_email_address(em); + onChange={(e) => { + set_email_address(e.target.value); }} maxLength={254} /> - +
{password_label} -
{ - e.preventDefault(); + { + const pw = e.target.value; + if (pw != null) { + setPassword(pw); + } + }} + onPressEnter={() => { if (is_submittable()) { return save_editing(); } }} - > - - { - const pw = ReactDOM.findDOMNode(passwordRef.current)?.value; - if (pw != null) { - setPassword(pw); - } - }} - /> - - - - {render_change_button()} + /> + - + + {render_error()} {render_saving()} -
+ ); } - function render_saving(): Rendered { + function render_saving() { if (state === "saving") { return ; } @@ -203,16 +173,12 @@ export const EmailAddressSetting = (props: Readonly) => { other {Set email address and password}}`, }, { - type: props.is_anonymous - ? "anonymous" - : props.email_address - ? "have_email" - : "", + type: is_anonymous ? "anonymous" : email_address ? "have_email" : "", }, ); } - const label = props.is_anonymous ? ( + const label = is_anonymous ? (
Sign up using an email address and password
@@ -223,13 +189,13 @@ export const EmailAddressSetting = (props: Readonly) => { return (
- {props.email_address} + {email_address} {state === "view" ? (