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