CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/account/settings/password-setting.tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { Form, Input } from "antd";
7
import { join } from "path";
8
import { useIntl } from "react-intl";
9
10
import { Button, ButtonToolbar, Well } from "@cocalc/frontend/antd-bootstrap";
11
import {
12
Rendered,
13
useIsMountedRef,
14
useState,
15
} from "@cocalc/frontend/app-framework";
16
import {
17
A,
18
ErrorDisplay,
19
LabeledRow,
20
Saving,
21
} from "@cocalc/frontend/components";
22
import { appBasePath } from "@cocalc/frontend/customize/app-base-path";
23
import { labels } from "@cocalc/frontend/i18n";
24
import { webapp_client } from "@cocalc/frontend/webapp-client";
25
26
interface State {
27
state: "view" | "edit" | "saving"; // view --> edit --> saving --> view
28
old_password: string;
29
new_password: string;
30
error: string;
31
}
32
33
export const PasswordSetting: React.FC = () => {
34
const intl = useIntl();
35
const is_mounted = useIsMountedRef();
36
37
const [state, set_state] = useState<State["state"]>("view");
38
const [old_password, set_old_password] = useState("");
39
const [new_password, set_new_password] = useState("");
40
const [error, set_error] = useState("");
41
42
function reset(): void {
43
set_state("view");
44
set_error("");
45
set_old_password("");
46
set_new_password("");
47
}
48
49
function change_password(): void {
50
reset();
51
set_state("edit");
52
}
53
54
function cancel_editing(): void {
55
set_state("view");
56
set_old_password("");
57
set_new_password("");
58
}
59
60
async function save_new_password(): Promise<void> {
61
set_state("saving");
62
try {
63
await webapp_client.account_client.change_password(
64
old_password,
65
new_password,
66
);
67
if (!is_mounted.current) return;
68
} catch (err) {
69
if (!is_mounted.current) return;
70
set_state("edit"), set_error(`Error changing password -- ${err}`);
71
return;
72
}
73
reset();
74
}
75
76
function is_submittable(): boolean {
77
return !!(
78
new_password.length >= 6 &&
79
new_password &&
80
new_password !== old_password
81
);
82
}
83
84
function render_change_button(): Rendered {
85
if (is_submittable()) {
86
return (
87
<Button onClick={save_new_password} bsStyle="success">
88
{intl.formatMessage(labels.account_password_change)}
89
</Button>
90
);
91
} else {
92
return (
93
<Button disabled bsStyle="success">
94
{intl.formatMessage(labels.account_password_change)}
95
</Button>
96
);
97
}
98
}
99
100
function render_error(): Rendered {
101
if (error) {
102
return (
103
<>
104
<ErrorDisplay
105
error={error}
106
onClose={() => set_error("")}
107
style={{ marginTop: "15px" }}
108
/>
109
<A href={join(appBasePath, "auth/password-reset")}>
110
{intl.formatMessage(labels.account_password_forgot)}
111
</A>
112
</>
113
);
114
}
115
}
116
117
function onFinish(): void {
118
if (is_submittable()) {
119
save_new_password();
120
}
121
}
122
123
function render_edit(): Rendered {
124
return (
125
<Well style={{ marginTop: "3ex" }}>
126
<Form onFinish={onFinish}>
127
<Form.Item>
128
Current password{" "}
129
<span color="#888">
130
(leave blank if you have not set a password)
131
</span>
132
<Input.Password
133
autoFocus
134
type="password"
135
value={old_password}
136
placeholder="Current password"
137
onChange={(e) => set_old_password(e.target.value)}
138
/>
139
</Form.Item>
140
New password
141
{new_password.length < 6 ? " (at least 6 characters)" : undefined}
142
{new_password.length >= 6 && new_password == old_password
143
? " (different than old password)"
144
: undefined}
145
<Form.Item>
146
<Input.Password
147
type="password"
148
value={new_password}
149
placeholder="New password"
150
onChange={(e) => {
151
set_new_password(e.target.value);
152
}}
153
/>
154
</Form.Item>
155
</Form>
156
<ButtonToolbar>
157
{render_change_button()}
158
<Button onClick={cancel_editing}>Cancel</Button>
159
</ButtonToolbar>
160
{render_error()}
161
{render_saving()}
162
</Well>
163
);
164
}
165
166
function render_saving(): Rendered {
167
if (state === "saving") {
168
return <Saving />;
169
}
170
}
171
172
return (
173
<LabeledRow
174
label={intl.formatMessage(labels.account_password)}
175
style={{ marginBottom: "15px" }}
176
>
177
<div style={{ height: "30px" }}>
178
<Button
179
className="pull-right"
180
disabled={state !== "view"}
181
onClick={change_password}
182
>
183
{intl.formatMessage(labels.account_password_change)}...
184
</Button>
185
</div>
186
{state !== "view" ? render_edit() : undefined}
187
</LabeledRow>
188
);
189
};
190
191