password reset works now
This commit is contained in:
@@ -324,6 +324,11 @@ li.right:hover .menuTrigger {
|
|||||||
inset 0 -0.25rem 0.25rem -0.25rem rgba(0,0,0,0.15);
|
inset 0 -0.25rem 0.25rem -0.25rem rgba(0,0,0,0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Element which can take focus but shows no outline */
|
||||||
|
.group-focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* END generic text colors */
|
/* END generic text colors */
|
||||||
|
|
||||||
/* END of basic elements */
|
/* END of basic elements */
|
||||||
|
|||||||
+118
-47
@@ -193,6 +193,21 @@ function base64DecodedLen(n) {
|
|||||||
return Math.floor(n / 4) * 3;
|
return Math.floor(n / 4) * 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-encode string to standard base64 encoding with padding.
|
||||||
|
// The string may be base64-URL encoded without the padding.
|
||||||
|
function base64ReEncode(str) {
|
||||||
|
if (str) {
|
||||||
|
str = str.replace("-", "+").replace("_", "/");
|
||||||
|
try {
|
||||||
|
str = btoa(atob(str));
|
||||||
|
} catch(err) {
|
||||||
|
console.log("failed to base64 re-decode string");
|
||||||
|
str = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert uploaded image into a base64-encoded string possibly scaling
|
// Convert uploaded image into a base64-encoded string possibly scaling
|
||||||
// linear dimensions or constraining to a square.
|
// linear dimensions or constraining to a square.
|
||||||
function imageFileScaledToBase64(file, width, height, forceSquare, onSuccess, onError) {
|
function imageFileScaledToBase64(file, width, height, forceSquare, onSuccess, onError) {
|
||||||
@@ -752,6 +767,7 @@ class InPlaceEdit extends React.Component {
|
|||||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
this.handleStartEditing = this.handleStartEditing.bind(this);
|
this.handleStartEditing = this.handleStartEditing.bind(this);
|
||||||
this.handleEditingFinished = this.handleEditingFinished.bind(this);
|
this.handleEditingFinished = this.handleEditingFinished.bind(this);
|
||||||
|
this.handlePasswordFinished = this.handlePasswordFinished.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(nextProps, prevState) {
|
static getDerivedStateFromProps(nextProps, prevState) {
|
||||||
@@ -795,8 +811,17 @@ class InPlaceEdit extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePasswordFinished(value) {
|
||||||
|
this.setState({active: false});
|
||||||
|
if (value && (value !== this.props.value)) {
|
||||||
|
this.props.onFinished(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.state.active) {
|
if (this.state.active) {
|
||||||
|
var fieldType = this.props.type || "text";
|
||||||
|
} else {
|
||||||
var spanText = this.props.type == "password" ? "••••••••" : this.state.value;
|
var spanText = this.props.type == "password" ? "••••••••" : this.state.value;
|
||||||
var spanClass = "in-place-edit" +
|
var spanClass = "in-place-edit" +
|
||||||
(this.props.readOnly ? " disabled" : "");
|
(this.props.readOnly ? " disabled" : "");
|
||||||
@@ -810,15 +835,25 @@ class InPlaceEdit extends React.Component {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
this.state.active ?
|
this.state.active ?
|
||||||
<input type={this.props.type || "text"}
|
(fieldType == "password" ?
|
||||||
value={this.state.value}
|
<VisiblePassword
|
||||||
placeholder={this.props.placeholder}
|
value={this.state.value}
|
||||||
required={this.props.required ? 'required' : ''}
|
placeholder={this.props.placeholder}
|
||||||
autoComplete={this.props.autoComplete}
|
required={this.props.required ? 'required' : ''}
|
||||||
autoFocus
|
autoComplete={this.props.autoComplete}
|
||||||
onChange={this.handeTextChange}
|
autoFocus={true}
|
||||||
onKeyDown={this.handleKeyDown}
|
onFinished={this.handlePasswordFinished}/>
|
||||||
onBlur={this.handleEditingFinished} />
|
:
|
||||||
|
<input type={fieldType}
|
||||||
|
value={this.state.value}
|
||||||
|
placeholder={this.props.placeholder}
|
||||||
|
required={this.props.required ? 'required' : ''}
|
||||||
|
autoComplete={this.props.autoComplete}
|
||||||
|
autoFocus
|
||||||
|
onChange={this.handeTextChange}
|
||||||
|
onKeyDown={this.handleKeyDown}
|
||||||
|
onBlur={this.handleEditingFinished} />
|
||||||
|
)
|
||||||
:
|
:
|
||||||
<span className={spanClass} onClick={this.handleStartEditing}>
|
<span className={spanClass} onClick={this.handleStartEditing}>
|
||||||
<span className="content">{spanText}</span>
|
<span className="content">{spanText}</span>
|
||||||
@@ -855,24 +890,37 @@ class VisiblePassword extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleKeyDown(e) {
|
handleKeyDown(e) {
|
||||||
if (e.keyCode === 27) {
|
if (e.keyCode == 27) {
|
||||||
// Escape pressed
|
// Escape pressed
|
||||||
this.setState({value: this.props.value, active: false});
|
this.setState({value: this.props.value, visible: false});
|
||||||
} else if (e.keyCode === 13) {
|
if (this.props.onFinished) {
|
||||||
|
this.props.onFinished();
|
||||||
|
}
|
||||||
|
} else if (e.keyCode == 13) {
|
||||||
// Enter pressed
|
// Enter pressed
|
||||||
this.handleEditingFinished();
|
this.handleEditingFinished();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditingFinished() {
|
handleEditingFinished(e) {
|
||||||
if (this.props.onFinished) {
|
if (e) {
|
||||||
this.props.onFinished(this.state.value);
|
let currentTarget = e.currentTarget;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!currentTarget.contains(document.activeElement)) {
|
||||||
|
if (this.props.onFinished) {
|
||||||
|
this.props.onFinished(this.state.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
} else if (this.props.onFinished) {
|
||||||
|
this.props.onFinished(this.state.value.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div tabIndex="-1" className="group-focus"
|
||||||
|
onBlur={this.handleEditingFinished}>
|
||||||
<input className="with-visibility"
|
<input className="with-visibility"
|
||||||
type={this.state.visible ? "text" : "password"}
|
type={this.state.visible ? "text" : "password"}
|
||||||
value={this.state.value}
|
value={this.state.value}
|
||||||
@@ -881,8 +929,7 @@ class VisiblePassword extends React.PureComponent {
|
|||||||
autoFocus={this.props.autoFocus ? 'autoFocus' : ''}
|
autoFocus={this.props.autoFocus ? 'autoFocus' : ''}
|
||||||
autoComplete={this.props.autoComplete}
|
autoComplete={this.props.autoComplete}
|
||||||
onChange={this.handeTextChange}
|
onChange={this.handeTextChange}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown} />
|
||||||
onBlur={this.handleEditingFinished} />
|
|
||||||
<span onClick={this.handleVisibility}>
|
<span onClick={this.handleVisibility}>
|
||||||
<i className="material-icons clickable light-gray">
|
<i className="material-icons clickable light-gray">
|
||||||
{this.state.visible ? 'visibility' : 'visibility_off'}
|
{this.state.visible ? 'visibility' : 'visibility_off'}
|
||||||
@@ -1531,14 +1578,20 @@ class PasswordResetView extends React.PureComponent {
|
|||||||
|
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
this.handleEmailChange = this.handleEmailChange.bind(this);
|
this.handleEmailChange = this.handleEmailChange.bind(this);
|
||||||
|
this.handlePasswordChange = this.handlePasswordChange.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
let parsed = parseUrlHash(window.location.hash);
|
||||||
|
this.setState({token: parsed.params.token, scheme: parsed.params.scheme});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(e) {
|
handleSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.props.token) {
|
if (this.state.token) {
|
||||||
this.props.onSubmit(null, this.state.password.trim());
|
this.props.onReset(this.state.scheme, this.state.password.trim(), this.state.token);
|
||||||
} else {
|
} else {
|
||||||
this.props.onSubmit("email", this.state.email.trim());
|
this.props.onRequest("email", this.state.email.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1551,10 +1604,17 @@ class PasswordResetView extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let buttonText = this.props.token ? "Reset" : "Send request";
|
let reset = (this.state.token && this.state.scheme);
|
||||||
return (
|
return (
|
||||||
<form id="password-reset-form" onSubmit={this.handleSubmit}>
|
<form id="password-reset-form" onSubmit={this.handleSubmit}>
|
||||||
{!this.props.token ?
|
{reset ?
|
||||||
|
<VisiblePassword
|
||||||
|
placeholder="Enter new password"
|
||||||
|
autoComplete="new-password"
|
||||||
|
value={this.state.password}
|
||||||
|
required={true} autoFocus={true}
|
||||||
|
onChange={this.handlePasswordChange} />
|
||||||
|
:
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<label htmlFor="inputEmail">Send a password reset email:</label>
|
<label htmlFor="inputEmail">Send a password reset email:</label>
|
||||||
<input type="email" id="inputEmail"
|
<input type="email" id="inputEmail"
|
||||||
@@ -1564,16 +1624,9 @@ class PasswordResetView extends React.PureComponent {
|
|||||||
onChange={this.handleEmailChange}
|
onChange={this.handleEmailChange}
|
||||||
required autoFocus />
|
required autoFocus />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
:
|
|
||||||
<VisiblePassword
|
|
||||||
placeholder="Enter new password"
|
|
||||||
autoComplete="new-password"
|
|
||||||
value={this.state.password}
|
|
||||||
required={true} autoFocus={true}
|
|
||||||
onChange={this.handlePasswordChange} />
|
|
||||||
}
|
}
|
||||||
<div className="dialog-buttons">
|
<div className="dialog-buttons">
|
||||||
<button className="blue" type="submit">{buttonText}</button>
|
<button className="blue" type="submit">{reset ? "Reset" : "Send request"}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
@@ -1832,7 +1885,8 @@ class SidepanelView extends React.Component {
|
|||||||
|
|
||||||
view === 'reset' ?
|
view === 'reset' ?
|
||||||
<PasswordResetView
|
<PasswordResetView
|
||||||
onSubmit={this.props.onPasswordReset} /> :
|
onRequest={this.props.onPasswordResetRequest}
|
||||||
|
onReset={this.props.onResetPassword} /> :
|
||||||
null}
|
null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -1890,8 +1944,8 @@ class EditAccountView extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
fullName: me.public ? me.public.fn : undefined,
|
fullName: me.public ? me.public.fn : undefined,
|
||||||
avatar: makeImageUrl(me.public ? me.public.photo : null),
|
avatar: makeImageUrl(me.public ? me.public.photo : null),
|
||||||
auth: defacs.auth,
|
auth: defacs ? defacs.auth : null,
|
||||||
anon: defacs.anon,
|
anon: defacs ? defacs.anon : null,
|
||||||
tags: fnd.tags(),
|
tags: fnd.tags(),
|
||||||
previousOnTags: fnd.onTagsUpdated
|
previousOnTags: fnd.onTagsUpdated
|
||||||
};
|
};
|
||||||
@@ -4474,6 +4528,7 @@ class TinodeWeb extends React.Component {
|
|||||||
this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);
|
this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);
|
||||||
this.handleValidateCredentialsRequest = this.handleValidateCredentialsRequest.bind(this);
|
this.handleValidateCredentialsRequest = this.handleValidateCredentialsRequest.bind(this);
|
||||||
this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this);
|
this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this);
|
||||||
|
this.handleResetPassword = this.handleResetPassword.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlankState() {
|
getBlankState() {
|
||||||
@@ -4571,11 +4626,11 @@ class TinodeWeb extends React.Component {
|
|||||||
this.tinode.enableLogging(true, true);
|
this.tinode.enableLogging(true, true);
|
||||||
this.tinode.onConnect = this.handleConnected;
|
this.tinode.onConnect = this.handleConnected;
|
||||||
this.tinode.onDisconnect = this.handleDisconnect;
|
this.tinode.onDisconnect = this.handleDisconnect;
|
||||||
let token, firebasePushToken;
|
|
||||||
if (localStorage.getObject("keep-logged-in")) {
|
|
||||||
token = localStorage.getObject("auth-token");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let token = localStorage.getObject("keep-logged-in") ?
|
||||||
|
localStorage.getObject("auth-token") : undefined;
|
||||||
|
|
||||||
|
let parsedNav = parseUrlHash(window.location.hash);
|
||||||
if (token) {
|
if (token) {
|
||||||
// When reading from storage, date is returned as string.
|
// When reading from storage, date is returned as string.
|
||||||
token.expires = new Date(token.expires);
|
token.expires = new Date(token.expires);
|
||||||
@@ -4584,12 +4639,11 @@ class TinodeWeb extends React.Component {
|
|||||||
// Socket error
|
// Socket error
|
||||||
this.handleError(err.message, "err");
|
this.handleError(err.message, "err");
|
||||||
});
|
});
|
||||||
var parsed = parseUrlHash(window.location.hash);
|
delete parsedNav.params.info;
|
||||||
delete parsed.params.info;
|
delete parsedNav.params.tab;
|
||||||
delete parsed.params.tab;
|
parsedNav.path[0] = '';
|
||||||
parsed.path[0] = '';
|
navigateTo(composeUrlHash(parsedNav.path, parsedNav.params));
|
||||||
navigateTo(composeUrlHash(parsed.path, parsed.params));
|
} else if (!parsedNav.params.token) {
|
||||||
} else {
|
|
||||||
navigateTo("");
|
navigateTo("");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5365,7 +5419,7 @@ class TinodeWeb extends React.Component {
|
|||||||
// If already connected, connnect() will return a resolved promise.
|
// If already connected, connnect() will return a resolved promise.
|
||||||
this.tinode.connect()
|
this.tinode.connect()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.tinode.requestResetAuthSecret("basic", method, value)
|
return this.tinode.requestResetAuthSecret("basic", method, value)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
// Socket error
|
// Socket error
|
||||||
@@ -5373,6 +5427,22 @@ class TinodeWeb extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleResetPassword(scheme, newPassword, token) {
|
||||||
|
token = base64ReEncode(token);
|
||||||
|
if (!token) {
|
||||||
|
this.handleError("Invalid security token", "err");
|
||||||
|
} else {
|
||||||
|
this.tinode.connect()
|
||||||
|
.then(() => {
|
||||||
|
return this.tinode.updateAccountBasic(null, null, newPassword, {token: token});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
// Socket error
|
||||||
|
this.handleError(err.message, "err");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div id="app-container">
|
<div id="app-container">
|
||||||
@@ -5430,7 +5500,8 @@ class TinodeWeb extends React.Component {
|
|||||||
onCancel={this.handleSidepanelCancel}
|
onCancel={this.handleSidepanelCancel}
|
||||||
onError={this.handleError}
|
onError={this.handleError}
|
||||||
onValidateCredentials={this.handleValidateCredentialsRequest}
|
onValidateCredentials={this.handleValidateCredentialsRequest}
|
||||||
onPasswordReset={this.handlePasswordResetRequest}
|
onPasswordResetRequest={this.handlePasswordResetRequest}
|
||||||
|
onResetPassword={this.handleResetPassword}
|
||||||
|
|
||||||
onInitFind={this.tnInitFind}
|
onInitFind={this.tnInitFind}
|
||||||
searchResults={this.state.searchResults}
|
searchResults={this.state.searchResults}
|
||||||
|
|||||||
Reference in New Issue
Block a user