diff --git a/@overflow/member/component/member-confirm-reset-password.component.html b/@overflow/member/component/member-confirm-reset-password.component.html index 6091c62..1a0f927 100644 --- a/@overflow/member/component/member-confirm-reset-password.component.html +++ b/@overflow/member/component/member-confirm-reset-password.component.html @@ -1,3 +1,32 @@ -
- Your email has been verified. When you click the link button below, it goes to the password change screen. +
+
+ + + + + +
diff --git a/@overflow/member/component/member-confirm-reset-password.component.ts b/@overflow/member/component/member-confirm-reset-password.component.ts index 22ef66c..58d428e 100644 --- a/@overflow/member/component/member-confirm-reset-password.component.ts +++ b/@overflow/member/component/member-confirm-reset-password.component.ts @@ -1,11 +1,49 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import { Store, select } from '@ngrx/store'; +import { Observable, of } from 'rxjs'; +import { catchError, exhaustMap, map, tap } from 'rxjs/operators'; +import {FormBuilder} from '@angular/forms'; +import {EmailAuthService} from '../service/email-auth.service'; +import {EmailAuth} from '@overflow/commons-typescript/model/email/EmailAuth'; @Component({ selector: 'of-member-confirm-reset-password', templateUrl: './member-confirm-reset-password.component.html', }) export class MemberConfirmResetPasswordComponent implements OnInit { - constructor() { } + emailAuth$: Observable; + pending$: Observable; + error$: Observable; + + @Input() token: string; + @Output() signin = new EventEmitter(); + @Output() modifyPassword = new EventEmitter(); + + constructor( + private store: Store, + private formBuilder: FormBuilder, + private emailAuthService: EmailAuthService, + ) { } ngOnInit(): void { + this.emailAuthService.readByPwAuthKey(this.token) + .pipe( + tap(() => { + this.pending$ = of(true); + }), + map((emailAuth: EmailAuth) => { + this.emailAuth$ = of(emailAuth); + }), + catchError( err => { + this.error$ = of(err); + return of(); + }), + tap(() => { + this.pending$ = of(false); + }), + ).take(1).subscribe(); + } + + onModifyPassword() { + this.modifyPassword.emit(); } } diff --git a/@overflow/member/component/member-confirm-signup.component.html b/@overflow/member/component/member-confirm-signup.component.html index d6c52f6..e6c38cb 100644 --- a/@overflow/member/component/member-confirm-signup.component.html +++ b/@overflow/member/component/member-confirm-signup.component.html @@ -1,3 +1,33 @@ -
- Your email has been verified. Go to signin when you click the link button below. +
+
+ + + + + +
diff --git a/@overflow/member/component/member-confirm-signup.component.ts b/@overflow/member/component/member-confirm-signup.component.ts index 3f4d0f1..b2a8459 100644 --- a/@overflow/member/component/member-confirm-signup.component.ts +++ b/@overflow/member/component/member-confirm-signup.component.ts @@ -1,11 +1,48 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import { Store, select } from '@ngrx/store'; +import { Observable, of } from 'rxjs'; +import { catchError, exhaustMap, map, tap } from 'rxjs/operators'; +import {FormBuilder} from '@angular/forms'; +import {EmailAuthService} from '../service/email-auth.service'; +import {EmailAuth} from '@overflow/commons-typescript/model/email/EmailAuth'; @Component({ selector: 'of-member-confirm-signup', templateUrl: './member-confirm-signup.component.html', }) export class MemberConfirmSignupComponent implements OnInit { - constructor() { } + @Input() token: string; + @Output() signin = new EventEmitter(); + + emailAuth$: Observable; + pending$: Observable; + error$: Observable; + + constructor( + private store: Store, + private formBuilder: FormBuilder, + private emailAuthService: EmailAuthService, + ) { } ngOnInit(): void { + this.emailAuthService.readBySignupAuthKey(this.token) + .pipe( + tap(() => { + this.pending$ = of(true); + }), + map((emailAuth: EmailAuth) => { + this.emailAuth$ = of(emailAuth); + }), + catchError( err => { + this.error$ = of(err); + return of(); + }), + tap(() => { + this.pending$ = of(false); + }), + ).take(1).subscribe(); + } + + onSignin() { + this.signin.emit(); } } diff --git a/@overflow/member/component/member-modify-password.component.html b/@overflow/member/component/member-modify-password.component.html index c6ca618..cf7c5df 100644 --- a/@overflow/member/component/member-modify-password.component.html +++ b/@overflow/member/component/member-modify-password.component.html @@ -1,3 +1,4 @@ +
@@ -51,7 +52,38 @@
- -
- Password Modify Complete + +
+
+ + + + + +
diff --git a/@overflow/member/component/member-modify-password.component.ts b/@overflow/member/component/member-modify-password.component.ts index 144ce9c..6ab0839 100644 --- a/@overflow/member/component/member-modify-password.component.ts +++ b/@overflow/member/component/member-modify-password.component.ts @@ -1,16 +1,26 @@ import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core'; import {FormGroup, FormBuilder, Validators, FormControl, ValidationErrors, AbstractControl} from '@angular/forms'; -import {Member} from '@overflow/commons-typescript/model/member'; +import { Member } from '@overflow/commons-typescript/model/member'; +import { Store, select } from '@ngrx/store'; +import { Observable, of } from 'rxjs'; +import { catchError, exhaustMap, map, tap } from 'rxjs/operators'; + +import { MemberService } from '../service/member.service'; + @Component({ selector: 'of-member-modify-password', templateUrl: './member-modify-password.component.html', }) export class MemberModifyPasswordComponent implements OnInit { - @Input() token: string; - @Input() member: Member; + member$: Observable; + pending$: Observable; + error$: Observable; - @Output() modifyPassword = new EventEmitter<{token: string, password: string, confirmPassword: string}>(); + @Input() token: string; + // @Input() member: Member; + + // @Output() modifyPassword = new EventEmitter<{token: string, password: string, confirmPassword: string}>(); @Output() signin = new EventEmitter(); modifyPasswordForm: FormGroup; @@ -18,7 +28,9 @@ export class MemberModifyPasswordComponent implements OnInit { pwConfirm: AbstractControl; constructor( + private store: Store, private formBuilder: FormBuilder, + private memberService: MemberService, ) { } ngOnInit() { @@ -63,7 +75,23 @@ export class MemberModifyPasswordComponent implements OnInit { modifyPasswordFormSubmit() { const formValue = Object.assign({}, this.modifyPasswordForm.value); - this.modifyPassword.emit({token: this.token, password: formValue.pw, confirmPassword: formValue.confirmPw}); + // this.modifyPassword.emit({token: this.token, password: formValue.pw, confirmPassword: formValue.confirmPw}); + this.memberService.resetPassword(this.token, formValue.password, formValue.pwConfirm) + .pipe( + tap(() => { + this.pending$ = of(true); + }), + map((rmember: Member) => { + this.member$ = of(rmember); + }), + catchError( err => { + this.error$ = of(err); + return of(); + }), + tap(() => { + this.pending$ = of(false); + }), + ).take(1).subscribe(); } onSignin() { diff --git a/@overflow/member/component/member-reset-password.component.html b/@overflow/member/component/member-reset-password.component.html index b5a27b0..6fb94d5 100644 --- a/@overflow/member/component/member-reset-password.component.html +++ b/@overflow/member/component/member-reset-password.component.html @@ -1,45 +1,86 @@ -
-
-
- - - + + + +
-
-
- I sent an authentication mail to the mail address you registered. Please check and change your password. +
+ +
+
+ + + + + +
diff --git a/@overflow/member/component/member-reset-password.component.ts b/@overflow/member/component/member-reset-password.component.ts index 00ab882..2738bec 100644 --- a/@overflow/member/component/member-reset-password.component.ts +++ b/@overflow/member/component/member-reset-password.component.ts @@ -10,24 +10,34 @@ import { Validators, AbstractControl } from '@angular/forms'; +import { Store, select } from '@ngrx/store'; +import { Observable, of } from 'rxjs'; +import { catchError, exhaustMap, map, tap } from 'rxjs/operators'; + import {Member} from '@overflow/commons-typescript/model/member'; +import {MemberService} from '../service/member.service'; @Component({ selector: 'of-member-reset-password', templateUrl: './member-reset-password.component.html', }) export class MemberResetPasswordComponent implements OnInit { - @Output() resetPassword = new EventEmitter(); + // @Output() resetPassword = new EventEmitter(); @Output() signin = new EventEmitter(); @Output() signup = new EventEmitter(); - @Input() member: Member; + // @Input() member: Member; + member$: Observable; + pending$: Observable; + error$: Observable; resetPasswordForm: FormGroup; email: AbstractControl; constructor( - private formBuilder: FormBuilder + private store: Store, + private formBuilder: FormBuilder, + private memberService: MemberService, ) { } @@ -45,7 +55,23 @@ export class MemberResetPasswordComponent implements OnInit { resetPasswordSubmit() { const formValue = Object.assign({}, this.resetPasswordForm.value); - this.resetPassword.emit(formValue.email); + // this.resetPassword.emit(formValue.email); + this.memberService.sendEmailResetPassword(formValue.email) + .pipe( + tap(() => { + this.pending$ = of(true); + }), + map((rmember: Member) => { + this.member$ = of(rmember); + }), + catchError( err => { + this.error$ = of(err); + return of(); + }), + tap(() => { + this.pending$ = of(false); + }), + ).take(1).subscribe(); } onSignin() { @@ -55,4 +81,5 @@ export class MemberResetPasswordComponent implements OnInit { onSignup() { this.signup.emit(); } + } diff --git a/@overflow/member/component/member-signup.component.html b/@overflow/member/component/member-signup.component.html index e8708f2..4f53b26 100644 --- a/@overflow/member/component/member-signup.component.html +++ b/@overflow/member/component/member-signup.component.html @@ -17,7 +17,8 @@ -
+ +
@@ -165,9 +166,9 @@ + - -
+
@@ -186,7 +187,10 @@

신청하신 이메일 주소는 {{member.email}} 입니다.

- @@ -197,4 +201,4 @@
-
\ No newline at end of file +
diff --git a/@overflow/member/component/member-signup.component.ts b/@overflow/member/component/member-signup.component.ts index d8aac22..30b2180 100644 --- a/@overflow/member/component/member-signup.component.ts +++ b/@overflow/member/component/member-signup.component.ts @@ -7,15 +7,22 @@ import { } from '@angular/core'; import { FormGroup, FormBuilder, Validators, AbstractControl, FormControl, ValidationErrors } from '@angular/forms'; import { Member } from '@overflow/commons-typescript/model/member'; +import { Store, select } from '@ngrx/store'; +import { Observable, of } from 'rxjs'; +import { catchError, exhaustMap, map, tap } from 'rxjs/operators'; +import {MemberService} from '../service/member.service'; @Component({ selector: 'of-member-signup', templateUrl: './member-signup.component.html', }) export class MemberSignupComponent implements OnInit, OnDestroy { - @Output() signup = new EventEmitter<{member: Member, password: string}>(); + // @Output() signup = new EventEmitter<{member: Member, password: string}>(); + @Output() signin = new EventEmitter(); - @Input() member; + member$: Observable; + pending$: Observable; + error$: Observable; signupForm: FormGroup; email: AbstractControl; @@ -30,7 +37,9 @@ export class MemberSignupComponent implements OnInit, OnDestroy { policyDisplay = false; constructor( + private store: Store, private formBuilder: FormBuilder, + private memberService: MemberService, ) { } @@ -122,7 +131,22 @@ export class MemberSignupComponent implements OnInit, OnDestroy { phone: signupValue.phone, companyName: signupValue.company, }; - this.signup.emit({member: member, password: password}); + this.memberService.signup(member, password) + .pipe( + tap(() => { + this.pending$ = of(true); + }), + map((rmember: Member) => { + this.member$ = of(rmember); + }), + catchError( err => { + this.error$ = of(err); + return of(); + }), + tap(() => { + this.pending$ = of(false); + }), + ).take(1).subscribe(); } termsDisplayOpen() { @@ -140,4 +164,8 @@ export class MemberSignupComponent implements OnInit, OnDestroy { onScriptError() { console.log('Something went long when loading the Google reCAPTCHA'); } + + onSignin() { + this.signin.emit(); + } } diff --git a/@overflow/member/container/member-confirm-reset-password-container.component.html b/@overflow/member/container/member-confirm-reset-password-container.component.html index 9a84edd..46c31fe 100644 --- a/@overflow/member/container/member-confirm-reset-password-container.component.html +++ b/@overflow/member/container/member-confirm-reset-password-container.component.html @@ -1 +1 @@ - + diff --git a/@overflow/member/container/member-confirm-signup-container.component.html b/@overflow/member/container/member-confirm-signup-container.component.html index ca864f3..88b5c58 100644 --- a/@overflow/member/container/member-confirm-signup-container.component.html +++ b/@overflow/member/container/member-confirm-signup-container.component.html @@ -1 +1 @@ - + diff --git a/@overflow/member/container/member-modify-password-container.component.html b/@overflow/member/container/member-modify-password-container.component.html index 6c14e57..f657a77 100644 --- a/@overflow/member/container/member-modify-password-container.component.html +++ b/@overflow/member/container/member-modify-password-container.component.html @@ -1,7 +1,7 @@ - - + + + + + + + diff --git a/@overflow/member/container/member-reset-password-container.component.html b/@overflow/member/container/member-reset-password-container.component.html index 6e31e48..15e3e06 100644 --- a/@overflow/member/container/member-reset-password-container.component.html +++ b/@overflow/member/container/member-reset-password-container.component.html @@ -1,7 +1,7 @@ - - + + + + + + + diff --git a/@overflow/member/container/member-signup-container.component.html b/@overflow/member/container/member-signup-container.component.html index 9db433e..54b8014 100644 --- a/@overflow/member/container/member-signup-container.component.html +++ b/@overflow/member/container/member-signup-container.component.html @@ -1 +1 @@ - + diff --git a/@overflow/member/service/email-auth.service.ts b/@overflow/member/service/email-auth.service.ts new file mode 100644 index 0000000..b2cdc8a --- /dev/null +++ b/@overflow/member/service/email-auth.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; + +import 'rxjs/add/operator/map'; + +import { RESTService } from '@loafer/ng-rest'; + +@Injectable() +export class EmailAuthService { + + public constructor( + private restService: RESTService, + ) {} + + public readBySignupAuthKey(token: string): Observable { + return this.restService.request('post', '/account/confirm_email', { + body: { + token: token, + }, + }); + } + + public readByPwAuthKey(token: string): Observable { + return this.restService.request('post', '/account/confirm_reset_pw', { + body: { + token: token, + }, + }); + } +} diff --git a/@overflow/member/service/index.ts b/@overflow/member/service/index.ts index 60b23b3..8ce8aac 100644 --- a/@overflow/member/service/index.ts +++ b/@overflow/member/service/index.ts @@ -1,7 +1,9 @@ import { MemberService } from './member.service'; -import {MemberTotpService} from './member-totp.service'; +import { MemberTotpService } from './member-totp.service'; +import { EmailAuthService } from './email-auth.service'; export const SERVICES = [ MemberService, MemberTotpService, + EmailAuthService, ]; diff --git a/@overflow/member/store/container/email-auth/email-auth.reducer.ts b/@overflow/member/store/container/email-auth/email-auth.reducer.ts new file mode 100644 index 0000000..e47bd13 --- /dev/null +++ b/@overflow/member/store/container/email-auth/email-auth.reducer.ts @@ -0,0 +1,65 @@ +import { + Actions, + ActionType, +} from '../../entity/member/member.action'; + +import { + State, + initialState, +} from './email-auth.state'; + +export function reducer(state = initialState, action: Actions): State { + switch (action.type) { + case ActionType.ConfirmEmailForSignup: { + return { + emailAuth: null, + pending: true, + error: null, + }; + } + + case ActionType.ConfirmEmailForSignupSuccess: { + return { + emailAuth: action.payload, + pending: false, + error: null, + }; + } + + case ActionType.ConfirmEmailForSignupFailure: { + return { + emailAuth: null, + pending: false, + error: action.payload, + }; + } + + case ActionType.ConfirmEmailForPassword: { + return { + emailAuth: null, + pending: true, + error: null, + }; + } + + case ActionType.ConfirmEmailForPasswordSuccess: { + return { + emailAuth: action.payload, + pending: true, + error: null, + }; + } + + case ActionType.ConfirmEmailForPasswordFailure: { + return { + emailAuth: null, + pending: true, + error: action.payload, + }; + } + + default: { + return state; + } + } +} diff --git a/@overflow/member/store/container/email-auth/email-auth.state.ts b/@overflow/member/store/container/email-auth/email-auth.state.ts new file mode 100644 index 0000000..89648da --- /dev/null +++ b/@overflow/member/store/container/email-auth/email-auth.state.ts @@ -0,0 +1,23 @@ +import { Selector, createSelector } from '@ngrx/store'; +import { RESTClientError } from '@loafer/ng-rest'; +import { EmailAuth } from '@overflow/commons-typescript/model/email/EmailAuth'; + +export interface State { + emailAuth: EmailAuth; + pending: boolean; + error: RESTClientError; +} + +export const initialState: State = { + emailAuth: null, + pending: false, + error: null, +}; + +export function getSelectors(selector: Selector) { + return { + selectEmailAuth: createSelector(selector, (state: State) => state.emailAuth), + selectPending: createSelector(selector, (state: State) => state.pending), + selectError: createSelector(selector, (state: State) => state.error), + }; +} diff --git a/@overflow/member/store/container/email-auth/index.ts b/@overflow/member/store/container/email-auth/index.ts new file mode 100644 index 0000000..9906e9b --- /dev/null +++ b/@overflow/member/store/container/email-auth/index.ts @@ -0,0 +1,3 @@ +export * from './email-auth.reducer'; +export * from './email-auth.state'; + diff --git a/@overflow/member/store/entity/member/member.action.ts b/@overflow/member/store/entity/member/member.action.ts index e479b08..667a54e 100644 --- a/@overflow/member/store/entity/member/member.action.ts +++ b/@overflow/member/store/entity/member/member.action.ts @@ -1,6 +1,7 @@ import { Action } from '@ngrx/store'; import { RESTClientError } from '@loafer/ng-rest'; import { Member } from '@overflow/commons-typescript/model/member'; +import {EmailAuth} from '@overflow/commons-typescript/model/email/EmailAuth'; export enum ActionType { Signin = '[member.member] Signin', @@ -21,6 +22,14 @@ export enum ActionType { ModifyPassword = '[member.member] ModifyPassword', ModifyPasswordSuccess = '[member.member] ModifyPasswordSuccess', ModifyPasswordFailure = '[member.member] ModifyPasswordFailure', + + ConfirmEmailForSignup = '[member.member] ConfirmEmailForSignup', + ConfirmEmailForSignupSuccess = '[member.member] ConfirmEmailForSignupSuccess', + ConfirmEmailForSignupFailure = '[member.member] ConfirmEmailForSignupFailure', + + ConfirmEmailForPassword = '[member.member] ConfirmEmailForPassword', + ConfirmEmailForPasswordSuccess = '[member.member] ConfirmEmailForPasswordSuccess', + ConfirmEmailForPasswordFailure = '[member.member] ConfirmEmailForPasswordFailure', } export class Signin implements Action { @@ -105,6 +114,42 @@ export class ModifyPasswordFailure implements Action { constructor(public payload: RESTClientError) {} } +export class ConfirmEmailForSignup implements Action { + readonly type = ActionType.ConfirmEmailForSignup; + + constructor(public payload: string ) {} +} + +export class ConfirmEmailForSignupSuccess implements Action { + readonly type = ActionType.ConfirmEmailForSignupSuccess; + + constructor(public payload: EmailAuth) {} +} + +export class ConfirmEmailForSignupFailure implements Action { + readonly type = ActionType.ConfirmEmailForSignupFailure; + + constructor(public payload: RESTClientError) {} +} + +export class ConfirmEmailForPassword implements Action { + readonly type = ActionType.ConfirmEmailForPassword; + + constructor(public payload: string) {} +} + +export class ConfirmEmailForPasswordSuccess implements Action { + readonly type = ActionType.ConfirmEmailForPasswordSuccess; + + constructor(public payload: EmailAuth) {} +} + +export class ConfirmEmailForPasswordFailure implements Action { + readonly type = ActionType.ConfirmEmailForPasswordFailure; + + constructor(public payload: RESTClientError) {} +} + export type Actions = | Signin | Signout @@ -120,4 +165,10 @@ export type Actions = | ModifyPassword | ModifyPasswordSuccess | ModifyPasswordFailure + | ConfirmEmailForSignup + | ConfirmEmailForSignupSuccess + | ConfirmEmailForSignupFailure + | ConfirmEmailForPassword + | ConfirmEmailForPasswordSuccess + | ConfirmEmailForPasswordFailure ; diff --git a/@overflow/member/store/entity/member/member.effect.ts b/@overflow/member/store/entity/member/member.effect.ts index 24fa967..b7cfd1d 100644 --- a/@overflow/member/store/entity/member/member.effect.ts +++ b/@overflow/member/store/entity/member/member.effect.ts @@ -22,6 +22,12 @@ import { ModifyPassword, ModifyPasswordSuccess, ModifyPasswordFailure, + ConfirmEmailForSignup, + ConfirmEmailForSignupSuccess, + ConfirmEmailForSignupFailure, + ConfirmEmailForPassword, + ConfirmEmailForPasswordSuccess, + ConfirmEmailForPasswordFailure, ActionType, } from './member.action'; @@ -32,6 +38,8 @@ import { SigninCookieFailure, } from '@overflow/shared/auth/store/container/auth'; import { DomainMember } from '@overflow/commons-typescript/model/domain'; +import { EmailAuthService } from '../../../service/email-auth.service'; +import {EmailAuth} from '@overflow/commons-typescript/model/email/EmailAuth'; @Injectable() @@ -40,6 +48,7 @@ export class Effects { constructor( private actions$: Actions, private memberService: MemberService, + private emailAuthService: EmailAuthService, private router: Router ) { } @@ -122,4 +131,36 @@ export class Effects { ) ) ); + + @Effect() + confirmEmailForSignup$ = this.actions$.pipe( + ofType(ActionType.ConfirmEmailForSignup), + map((action: ConfirmEmailForSignup) => action.payload), + exhaustMap(( token: string ) => + this.emailAuthService + .readBySignupAuthKey(token) + .pipe( + map( (emailAuth: EmailAuth) => { + return new ConfirmEmailForSignupSuccess(emailAuth); + }), + catchError(error => of(new ConfirmEmailForSignupFailure(error))) + ) + ) + ); + + @Effect() + confirmEmailForPassword$ = this.actions$.pipe( + ofType(ActionType.ConfirmEmailForPassword), + map((action: ConfirmEmailForPassword) => action.payload), + exhaustMap(( token: string ) => + this.emailAuthService + .readByPwAuthKey(token) + .pipe( + map( (emailAuth: EmailAuth) => { + return new ConfirmEmailForPasswordSuccess(emailAuth); + }), + catchError(error => of(new ConfirmEmailForPasswordFailure(error))) + ) + ) + ); } diff --git a/@overflow/member/store/index.ts b/@overflow/member/store/index.ts index 5dcc5d6..1c6e029 100644 --- a/@overflow/member/store/index.ts +++ b/@overflow/member/store/index.ts @@ -14,6 +14,7 @@ import * as MemberModifyPasswordContainerStore from './container/modify-password import * as MemberResetPasswordContainerStore from './container/reset-password'; import * as MemberModifyContainerStore from './container/modify'; import * as MemberSignoutContainerStore from './container/signout'; +import * as ConfirmEmailStore from './container/email-auth'; export interface State { @@ -23,6 +24,7 @@ export interface State { member_reset_password_container: MemberResetPasswordContainerStore.State; member_modify_container: MemberModifyContainerStore.State; member_signout_container: MemberSignoutContainerStore.State; + confirm_email_container: ConfirmEmailStore.State; } export const REDUCERS = { diff --git a/src/app/pages/auth/confirm/confirm-reset-password-page.component.html b/src/app/pages/auth/confirm/confirm-reset-password-page.component.html index 47f5cd5..d18b517 100644 --- a/src/app/pages/auth/confirm/confirm-reset-password-page.component.html +++ b/src/app/pages/auth/confirm/confirm-reset-password-page.component.html @@ -1,11 +1,11 @@
- + >