summaryrefslogtreecommitdiffstats
path: root/ui/src
diff options
context:
space:
mode:
authorDessalines <tyhou13@gmx.com>2019-11-01 23:41:57 -0700
committerDessalines <tyhou13@gmx.com>2019-11-01 23:41:57 -0700
commit68e4b61808ee76730cdc9f4302700a60a2ebb3b5 (patch)
tree8d36fb7b39b963696dc980f89f836b52c4aea20a /ui/src
parent9f35b33dc7238f0d6748beafa79ca0af192b5ca6 (diff)
Password reset mostly working.
Diffstat (limited to 'ui/src')
-rw-r--r--ui/src/components/login.tsx10
-rw-r--r--ui/src/components/password_change.tsx160
-rw-r--r--ui/src/index.tsx5
-rw-r--r--ui/src/services/WebSocketService.ts5
-rw-r--r--ui/src/translations/en.ts2
-rw-r--r--ui/src/utils.ts5
6 files changed, 183 insertions, 4 deletions
diff --git a/ui/src/components/login.tsx b/ui/src/components/login.tsx
index c2db7ee6..8d0df3e3 100644
--- a/ui/src/components/login.tsx
+++ b/ui/src/components/login.tsx
@@ -9,7 +9,7 @@ import {
PasswordResetForm,
} from '../interfaces';
import { WebSocketService, UserService } from '../services';
-import { msgOp } from '../utils';
+import { msgOp, validEmail } from '../utils';
import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
@@ -113,12 +113,13 @@ export class Login extends Component<any, State> {
class="form-control"
required
/>
- <div
+ <button
+ disabled={!validEmail(this.state.loginForm.username_or_email)}
onClick={linkEvent(this, this.handlePasswordReset)}
- class="pointer d-inline-block float-right text-muted small font-weight-bold"
+ className="btn p-0 btn-link d-inline-block float-right text-muted small font-weight-bold"
>
<T i18nKey="forgot_password">#</T>
- </div>
+ </button>
</div>
</div>
<div class="form-group row">
@@ -287,6 +288,7 @@ export class Login extends Component<any, State> {
}
handlePasswordReset(i: Login) {
+ event.preventDefault();
let resetForm: PasswordResetForm = {
email: i.state.loginForm.username_or_email,
};
diff --git a/ui/src/components/password_change.tsx b/ui/src/components/password_change.tsx
new file mode 100644
index 00000000..3e542f7b
--- /dev/null
+++ b/ui/src/components/password_change.tsx
@@ -0,0 +1,160 @@
+import { Component, linkEvent } from 'inferno';
+import { Subscription } from 'rxjs';
+import { retryWhen, delay, take } from 'rxjs/operators';
+import {
+ UserOperation,
+ LoginResponse,
+ PasswordChangeForm,
+} from '../interfaces';
+import { WebSocketService, UserService } from '../services';
+import { msgOp, capitalizeFirstLetter } from '../utils';
+import { i18n } from '../i18next';
+import { T } from 'inferno-i18next';
+
+interface State {
+ passwordChangeForm: PasswordChangeForm;
+ loading: boolean;
+}
+
+export class PasswordChange extends Component<any, State> {
+ private subscription: Subscription;
+
+ emptyState: State = {
+ passwordChangeForm: {
+ token: this.props.match.params.token,
+ password: undefined,
+ password_verify: undefined,
+ },
+ loading: false,
+ };
+
+ constructor(props: any, context: any) {
+ super(props, context);
+
+ this.state = this.emptyState;
+
+ this.subscription = WebSocketService.Instance.subject
+ .pipe(
+ retryWhen(errors =>
+ errors.pipe(
+ delay(3000),
+ take(10)
+ )
+ )
+ )
+ .subscribe(
+ msg => this.parseMessage(msg),
+ err => console.error(err),
+ () => console.log('complete')
+ );
+ }
+
+ componentWillUnmount() {
+ this.subscription.unsubscribe();
+ }
+
+ componentDidMount() {
+ document.title = `${i18n.t('password_change')} - ${
+ WebSocketService.Instance.site.name
+ }`;
+ }
+
+ render() {
+ return (
+ <div class="container">
+ <div class="row">
+ <div class="col-12 col-lg-6 offset-lg-3 mb-4">
+ <h5>
+ <T i18nKey="password_change">#</T>
+ </h5>
+ {this.passwordChangeForm()}
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ passwordChangeForm() {
+ return (
+ <form onSubmit={linkEvent(this, this.handlePasswordChangeSubmit)}>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="new_password">#</T>
+ </label>
+ <div class="col-sm-10">
+ <input
+ type="password"
+ value={this.state.passwordChangeForm.password}
+ onInput={linkEvent(this, this.handlePasswordChange)}
+ class="form-control"
+ required
+ />
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">
+ <T i18nKey="verify_password">#</T>
+ </label>
+ <div class="col-sm-10">
+ <input
+ type="password"
+ value={this.state.passwordChangeForm.password_verify}
+ onInput={linkEvent(this, this.handleVerifyPasswordChange)}
+ class="form-control"
+ required
+ />
+ </div>
+ </div>
+ <div class="form-group row">
+ <div class="col-sm-10">
+ <button type="submit" class="btn btn-secondary">
+ {this.state.loading ? (
+ <svg class="icon icon-spinner spin">
+ <use xlinkHref="#icon-spinner"></use>
+ </svg>
+ ) : (
+ capitalizeFirstLetter(i18n.t('save'))
+ )}
+ </button>
+ </div>
+ </div>
+ </form>
+ );
+ }
+
+ handlePasswordChange(i: PasswordChange, event: any) {
+ i.state.passwordChangeForm.password = event.target.value;
+ i.setState(i.state);
+ }
+
+ handleVerifyPasswordChange(i: PasswordChange, event: any) {
+ i.state.passwordChangeForm.password_verify = event.target.value;
+ i.setState(i.state);
+ }
+
+ handlePasswordChangeSubmit(i: PasswordChange, event: any) {
+ event.preventDefault();
+ i.state.loading = true;
+ i.setState(i.state);
+
+ WebSocketService.Instance.passwordChange(i.state.passwordChangeForm);
+ }
+
+ parseMessage(msg: any) {
+ let op: UserOperation = msgOp(msg);
+ if (msg.error) {
+ alert(i18n.t(msg.error));
+ this.state.loading = false;
+ this.setState(this.state);
+ return;
+ } else {
+ if (op == UserOperation.PasswordChange) {
+ this.state = this.emptyState;
+ this.setState(this.state);
+ let res: LoginResponse = msg;
+ UserService.Instance.login(res);
+ this.props.history.push('/');
+ }
+ }
+ }
+}
diff --git a/ui/src/index.tsx b/ui/src/index.tsx
index f3c7ff38..2e50db88 100644
--- a/ui/src/index.tsx
+++ b/ui/src/index.tsx
@@ -7,6 +7,7 @@ import { Footer } from './components/footer';
import { Login } from './components/login';
import { CreatePost } from './components/create-post';
import { CreateCommunity } from './components/create-community';
+import { PasswordChange } from './components/password_change';
import { Post } from './components/post';
import { Community } from './components/community';
import { Communities } from './components/communities';
@@ -74,6 +75,10 @@ class Index extends Component<any, any> {
/>
<Route path={`/search`} component={Search} />
<Route path={`/sponsors`} component={Sponsors} />
+ <Route
+ path={`/password_change/:token`}
+ component={PasswordChange}
+ />
</Switch>
<Symbols />
</div>
diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts
index c77816df..34da5850 100644
--- a/ui/src/services/WebSocketService.ts
+++ b/ui/src/services/WebSocketService.ts
@@ -31,6 +31,7 @@ import {
UserSettingsForm,
DeleteAccountForm,
PasswordResetForm,
+ PasswordChangeForm,
} from '../interfaces';
import { webSocket } from 'rxjs/webSocket';
import { Subject } from 'rxjs';
@@ -279,6 +280,10 @@ export class WebSocketService {
this.subject.next(this.wsSendWrapper(UserOperation.PasswordReset, form));
}
+ public passwordChange(form: PasswordChangeForm) {
+ this.subject.next(this.wsSendWrapper(UserOperation.PasswordChange, form));
+ }
+
private wsSendWrapper(op: UserOperation, data: any) {
let send = { op: UserOperation[op], data: data };
console.log(send);
diff --git a/ui/src/translations/en.ts b/ui/src/translations/en.ts
index 4e0d81db..f73e0d09 100644
--- a/ui/src/translations/en.ts
+++ b/ui/src/translations/en.ts
@@ -118,6 +118,8 @@ export const en = {
verify_password: 'Verify Password',
forgot_password: 'forgot password',
reset_password_mail_sent: 'Sent an Email to reset your password.',
+ password_change: 'Password Change',
+ new_password: 'New Password',
no_email_setup: "This server hasn't correctly set up email.",
email: 'Email',
optional: 'Optional',
diff --git a/ui/src/utils.ts b/ui/src/utils.ts
index b9220a2d..9d2e720e 100644
--- a/ui/src/utils.ts
+++ b/ui/src/utils.ts
@@ -152,6 +152,11 @@ export function validURL(str: string) {
}
}
+export function validEmail(email: string) {
+ let re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+ return re.test(String(email).toLowerCase());
+}
+
export function capitalizeFirstLetter(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}