diff options
author | Dessalines <tyhou13@gmx.com> | 2019-11-01 23:41:57 -0700 |
---|---|---|
committer | Dessalines <tyhou13@gmx.com> | 2019-11-01 23:41:57 -0700 |
commit | 68e4b61808ee76730cdc9f4302700a60a2ebb3b5 (patch) | |
tree | 8d36fb7b39b963696dc980f89f836b52c4aea20a /ui | |
parent | 9f35b33dc7238f0d6748beafa79ca0af192b5ca6 (diff) |
Password reset mostly working.
Diffstat (limited to 'ui')
-rw-r--r-- | ui/src/components/login.tsx | 10 | ||||
-rw-r--r-- | ui/src/components/password_change.tsx | 160 | ||||
-rw-r--r-- | ui/src/index.tsx | 5 | ||||
-rw-r--r-- | ui/src/services/WebSocketService.ts | 5 | ||||
-rw-r--r-- | ui/src/translations/en.ts | 2 | ||||
-rw-r--r-- | ui/src/utils.ts | 5 |
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); } |