refactoring of login process

This commit is contained in:
병준 박 2019-10-02 14:34:17 +09:00
parent 7eb878cf68
commit 7c54e6606e
16 changed files with 489 additions and 501 deletions

View File

@ -1,7 +1,6 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; import { Routes, RouterModule } from '@angular/router';
import { AppAuthGuard } from './guards/auth.guard'; import { AppAuthGuard } from './guards/auth.guard';
import { AppMessengerResolver } from './resolvers/messenger.resolver';
const routes: Routes = [ const routes: Routes = [
{ path: '', redirectTo: '/messenger', pathMatch: 'full' }, { path: '', redirectTo: '/messenger', pathMatch: 'full' },
@ -9,10 +8,7 @@ const routes: Routes = [
path: 'messenger', path: 'messenger',
loadChildren: loadChildren:
'./pages/messenger/messenger.page.module#AppMessengerPageModule', './pages/messenger/messenger.page.module#AppMessengerPageModule',
canActivate: [AppAuthGuard], canActivate: [AppAuthGuard]
resolve: {
protocol: AppMessengerResolver
}
}, },
{ {
path: 'account', path: 'account',

View File

@ -2,12 +2,16 @@ import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; import { Routes, RouterModule } from '@angular/router';
import { MainPageComponent } from './components/main.page.component'; import { MainPageComponent } from './components/main.page.component';
import { AppMessengerResolver } from '@app/resolvers/messenger.resolver';
const routes: Routes = [ const routes: Routes = [
{ path: '', redirectTo: 'main', pathMatch: 'full' }, { path: '', redirectTo: 'main', pathMatch: 'full' },
{ {
path: 'main', path: 'main',
component: MainPageComponent component: MainPageComponent,
resolve: {
protocol: AppMessengerResolver
}
} }
]; ];

View File

@ -4,17 +4,28 @@ import {
ActivatedRouteSnapshot, ActivatedRouteSnapshot,
RouterStateSnapshot RouterStateSnapshot
} from '@angular/router'; } from '@angular/router';
import { Observable, throwError } from 'rxjs'; import { Observable, throwError, of, EMPTY, forkJoin } from 'rxjs';
import { map, tap, mergeMap, catchError, take } from 'rxjs/operators'; import {
map,
tap,
mergeMap,
catchError,
take,
exhaustMap,
concatMap,
switchMap,
withLatestFrom
} from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
import { ProtocolService } from '@ucap-webmessenger/protocol'; import { ProtocolService } from '@ucap-webmessenger/protocol';
import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { PublicApiService } from '@ucap-webmessenger/api-public'; import {
PublicApiService,
VersionInfo2Response
} from '@ucap-webmessenger/api-public';
import * as AppStore from '../store';
import * as VersionInfoStore from '../store/setting/version-info';
import { import {
LoginInfo, LoginInfo,
KEY_LOGIN_INFO, KEY_LOGIN_INFO,
@ -29,6 +40,17 @@ import {
} from '@ucap-webmessenger/protocol-authentication'; } from '@ucap-webmessenger/protocol-authentication';
import * as AuthenticationStore from '@app/store/account/authentication'; import * as AuthenticationStore from '@app/store/account/authentication';
import { ConnResponse } from 'projects/ucap-webmessenger-protocol-inner/src/lib/models/conn';
import { PiService } from '@ucap-webmessenger/pi';
import { NGXLogger } from 'ngx-logger';
import { QueryProtocolService } from '@ucap-webmessenger/protocol-query';
import { OptionProtocolService } from '@ucap-webmessenger/protocol-option';
import * as AppStore from '@app/store';
import * as VersionInfoStore from '@app/store/setting/version-info';
import * as OptionStore from '@app/store/setting/option';
import * as QueryStore from '@app/store/setting/query';
import * as SyncStore from '@app/store/messenger/sync';
@Injectable() @Injectable()
export class AppMessengerResolver implements Resolve<void> { export class AppMessengerResolver implements Resolve<void> {
@ -36,78 +58,115 @@ export class AppMessengerResolver implements Resolve<void> {
private store: Store<any>, private store: Store<any>,
private sessionStorageService: SessionStorageService, private sessionStorageService: SessionStorageService,
private publicApiService: PublicApiService, private publicApiService: PublicApiService,
private piService: PiService,
private protocolService: ProtocolService, private protocolService: ProtocolService,
private queryProtocolService: QueryProtocolService,
private optionProtocolService: OptionProtocolService,
private authenticationProtocolService: AuthenticationProtocolService, private authenticationProtocolService: AuthenticationProtocolService,
private innerProtocolService: InnerProtocolService private innerProtocolService: InnerProtocolService,
private logger: NGXLogger
) {} ) {}
resolve( resolve(
route: ActivatedRouteSnapshot, activatedRouteSnapshot: ActivatedRouteSnapshot,
state: RouterStateSnapshot routerStateSnapshot: RouterStateSnapshot
): void | Observable<void> | Promise<void> { ): void | Observable<void> | Promise<void> {
const loginInfo = this.sessionStorageService.get<LoginInfo>(KEY_LOGIN_INFO);
const environmentsInfo = this.sessionStorageService.get<EnvironmentsInfo>(
KEY_ENVIRONMENTS_INFO
);
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
resolve(); let versionInfo2Res: VersionInfo2Response;
// this.publicApiService let connRres: ConnResponse;
// .versionInfo2({ let loginRes: LoginResponse;
// deviceType: environmentsInfo.deviceType,
// companyGroupType: loginInfo.companyGroupType, const loginInfo = this.sessionStorageService.get<LoginInfo>(
// companyCode: loginInfo.companyCode, KEY_LOGIN_INFO
// loginId: loginInfo.loginId );
// }) const environmentsInfo = this.sessionStorageService.get<EnvironmentsInfo>(
// .pipe( KEY_ENVIRONMENTS_INFO
// take(1), );
// tap(versionInfo2Res => {
// this.store.dispatch(VersionInfoStore.fetchSuccess(versionInfo2Res)); this.publicApiService
// }), .versionInfo2({
// mergeMap(versionInfo2Res => deviceType: environmentsInfo.deviceType,
// this.protocolService.connect(versionInfo2Res.serverIp) companyGroupType: loginInfo.companyGroupType,
// ), companyCode: loginInfo.companyCode,
// mergeMap(() => this.innerProtocolService.conn({})), loginId: loginInfo.loginId
// mergeMap(connRres => })
// this.authenticationProtocolService.login({ .pipe(
// loginId: loginInfo.loginId, take(1),
// loginPw: loginInfo.loginPw, switchMap(res => {
// deviceType: environmentsInfo.deviceType, versionInfo2Res = res;
// deviceId: ' ', return this.protocolService.connect(versionInfo2Res.serverIp);
// token: '', }),
// localeCode: loginInfo.localeCode, switchMap(() => this.innerProtocolService.conn({})),
// pushId: ' ', switchMap(res => {
// companyCode: loginInfo.companyCode, connRres = res;
// passwordEncodingType: 1, return this.authenticationProtocolService.login({
// clientVersion: '', loginId: loginInfo.loginId,
// reconnect: false, loginPw: loginInfo.loginPw,
// ip: 'localhost', deviceType: environmentsInfo.deviceType,
// hostName: '', deviceId: ' ',
// ssoMode: SSOMode.AUTH, token: '',
// userSpecificInformation: 'PRO_000482', localeCode: loginInfo.localeCode,
// productId: 'PRO_000482', pushId: ' ',
// productName: 'EZMessenger' companyCode: loginInfo.companyCode,
// }) passwordEncodingType: 1,
// ), clientVersion: '',
// map(loginRes => { reconnect: false,
// this.store.dispatch( ip: 'localhost',
// AuthenticationStore.loginSuccess({ hostName: '',
// loginInfo: loginRes ssoMode: SSOMode.AUTH,
// }) userSpecificInformation: 'PRO_000482',
// ); productId: 'PRO_000482',
// }), productName: 'EZMessenger'
// catchError(err => { });
// return throwError(err); }),
// }) switchMap(res =>
// ) forkJoin([
// .subscribe( this.queryProtocolService.auth({
// () => { deviceType: environmentsInfo.deviceType
// resolve(); }),
// }, this.optionProtocolService.regView({})
// err => { ])
// reject(); ),
// } tap(([authRes, regViewRes]) => {
// ); this.store.dispatch(
OptionStore.regViewSuccess({ res: regViewRes })
);
this.store.dispatch(QueryStore.authSuccess({ res: authRes }));
}),
withLatestFrom(
this.store.pipe(
select(AppStore.MessengerSelector.SyncSelector.buddy2SyncDate)
),
this.store.pipe(
select(AppStore.MessengerSelector.SyncSelector.group2SyncDate)
)
),
map(([[], buddy2SyncDate, group2SyncDate]) => {
this.store.dispatch(SyncStore.buddy2({ syncDate: buddy2SyncDate }));
this.store.dispatch(SyncStore.group2({ syncDate: group2SyncDate }));
}),
catchError(err => {
return throwError(err);
})
)
.subscribe(
() => {
this.store.dispatch(
AuthenticationStore.loginSuccess({
loginInfo: loginRes
})
);
console.log('next');
resolve();
},
err => {
console.log('err', err);
reject(err);
},
() => {
console.log('complete');
}
);
}); });
} }
} }

View File

@ -1,27 +1,12 @@
import { Injectable, Inject } from '@angular/core'; import { Injectable, Inject } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { of, throwError, EMPTY as empty } from 'rxjs'; import { of } from 'rxjs';
import { import { catchError, exhaustMap, map, tap, switchMap } from 'rxjs/operators';
catchError,
exhaustMap,
map,
tap,
take,
switchMap,
mergeMap,
skip,
concatMap
} from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions, ofType, createEffect } from '@ngrx/effects'; import { Actions, ofType, createEffect } from '@ngrx/effects';
import { import { PiService, Login2Response } from '@ucap-webmessenger/pi';
PiService,
Login2Response,
ResponseStatus
} from '@ucap-webmessenger/pi';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import { import {
DialogService, DialogService,
@ -31,8 +16,6 @@ import {
} from '@ucap-webmessenger/ui'; } from '@ucap-webmessenger/ui';
import { import {
loginSuccess,
loginFailure,
loginRedirect, loginRedirect,
logout, logout,
logoutConfirmation, logoutConfirmation,
@ -41,265 +24,39 @@ import {
webLoginSuccess, webLoginSuccess,
webLoginFailure webLoginFailure
} from './actions'; } from './actions';
import { import { LoginInfo } from '../../../types';
LoginInfo,
EnvironmentsInfo,
KEY_ENVIRONMENTS_INFO
} from '../../../types';
import { AppAuthenticationService } from '../../../services/authentication.service'; import { AppAuthenticationService } from '../../../services/authentication.service';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
import {
PublicApiService,
VersionInfo2Response
} from '@ucap-webmessenger/api-public';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import * as VersionInfoStore from '@app/store/setting/version-info';
import { ProtocolService } from '@ucap-webmessenger/protocol';
import { InnerProtocolService } from '@ucap-webmessenger/protocol-inner';
import {
AuthenticationProtocolService,
SSOMode,
LoginResponse
} from '@ucap-webmessenger/protocol-authentication';
import CryptoJS from 'crypto-js';
@Injectable() @Injectable()
export class Effects { export class Effects {
// webLogin$ = createEffect(() => webLogin$ = createEffect(() =>
// this.actions$.pipe( this.actions$.pipe(
// ofType(webLogin), ofType(webLogin),
// map(action => action), map(action => action),
// exhaustMap((params: { loginInfo: LoginInfo; rememberMe: boolean }) => exhaustMap((params: { loginInfo: LoginInfo; rememberMe: boolean }) =>
// this.piService this.piService
// .login2({ .login2({
// loginId: params.loginInfo.loginId, loginId: params.loginInfo.loginId,
// loginPw: params.loginInfo.loginPw, loginPw: params.loginInfo.loginPw,
// companyCode: params.loginInfo.companyCode companyCode: params.loginInfo.companyCode
// }) })
// .pipe( .pipe(
// map((res: Login2Response) => { map((res: Login2Response) => {
// if ('success' !== res.status.toLowerCase()) { if ('success' !== res.status.toLowerCase()) {
// return webLoginFailure({ error: 'Failed' }); return webLoginFailure({ error: 'Failed' });
// } else { } else {
// return webLoginSuccess({ return webLoginSuccess({
// loginInfo: params.loginInfo, loginInfo: params.loginInfo,
// rememberMe: params.rememberMe, rememberMe: params.rememberMe,
// login2Response: res login2Response: res
// }); });
// } }
// }), }),
// catchError(error => of(webLoginFailure({ error }))) catchError(error => of(webLoginFailure({ error })))
// ) )
// ) )
// ) )
// );
webLogin$ = createEffect(
() =>
this.actions$.pipe(
ofType(webLogin),
map(action => action),
switchMap(
(loginParams: { loginInfo: LoginInfo; rememberMe: boolean }) => {
const environmentsInfo = this.sessionStorageService.get<
EnvironmentsInfo
>(KEY_ENVIRONMENTS_INFO);
let login2Res: Login2Response;
let loginRes: LoginResponse;
let versionInfo2Res: VersionInfo2Response;
let encLoginPw: string;
return this.piService
.login2({
loginId: loginParams.loginInfo.loginId,
loginPw: loginParams.loginInfo.loginPw,
companyCode: loginParams.loginInfo.companyCode
})
.pipe(
concatMap(res => {
console.log('tap');
if ('success' !== res.status.toLowerCase()) {
return throwError('login2 failed');
}
login2Res = res;
return of(true);
}),
mergeMap(dd => {
console.log('checkForUpdates');
return this.nativeService.checkForUpdates();
}),
concatMap(update => {
if (update) {
return throwError('update process');
}
encLoginPw = CryptoJS.enc.Hex.stringify(
CryptoJS.SHA256(loginParams.loginInfo.loginPw)
);
return of(true);
}),
mergeMap(() =>
this.publicApiService.versionInfo2({
deviceType: environmentsInfo.deviceType,
companyGroupType: loginParams.loginInfo.companyGroupType,
companyCode: loginParams.loginInfo.companyCode,
loginId: loginParams.loginInfo.loginId
})
),
map(res => {
this.store.dispatch(VersionInfoStore.fetchSuccess(res));
versionInfo2Res = res;
return of(true);
}),
mergeMap(() =>
this.protocolService.connect(versionInfo2Res.serverIp)
),
mergeMap(() => this.innerProtocolService.conn({})),
mergeMap(() =>
this.authenticationProtocolService.login({
loginId: loginParams.loginInfo.loginId,
loginPw: encLoginPw,
deviceType: environmentsInfo.deviceType,
deviceId: ' ',
token: '',
localeCode: loginParams.loginInfo.localeCode,
pushId: ' ',
companyCode: loginParams.loginInfo.companyCode,
passwordEncodingType: 1,
clientVersion: '',
reconnect: false,
ip: 'localhost',
hostName: '',
ssoMode: SSOMode.AUTH,
userSpecificInformation: 'PRO_000482',
productId: 'PRO_000482',
productName: 'EZMessenger'
})
),
exhaustMap(async res => {
if (res.privateInformationAgree) {
return of(null);
}
loginRes = res;
const privacyTotalUrl = this.piService.privacyTotalUrl({
companyCode: res.companyCode,
userSeq: res.userSeq,
token: res.tokenString,
deviceType: environmentsInfo.deviceType,
localeCode: loginParams.loginInfo.localeCode,
textOnly: 'true'
});
const result = await this.dialogService.open<
ConfirmDialogComponent,
ConfirmDialogData,
ConfirmDialogResult
>(ConfirmDialogComponent, {
width: '100%',
height: '500px',
disableClose: true,
data: {
title: '개인정보 동의',
html: `<iframe id="ifm_privacy" src="${privacyTotalUrl}" style="width: 100%;height: 300px;" />`
}
});
if (!result.choice) {
console.log('privacy error');
throw new Error('privacy error');
}
return of(true);
}),
exhaustMap(proceed => {
if (null === proceed) {
return of(null);
}
return this.piService.userTermsAction({
userSeq: loginRes.userSeq,
token: loginRes.tokenString,
deviceType: environmentsInfo.deviceType
});
}),
exhaustMap(async () => {
this.logger.debug(
'loginRes.passwordExpired',
loginRes.passwordExpired
);
if (!loginRes.passwordExpired) {
return of(null);
}
const privacyTotalUrl = this.piService.privacyTotalUrl({
companyCode: loginRes.companyCode,
userSeq: loginRes.userSeq,
token: loginRes.tokenString,
deviceType: environmentsInfo.deviceType,
localeCode: loginParams.loginInfo.localeCode,
textOnly: 'true'
});
const result = await this.dialogService.open<
ConfirmDialogComponent,
ConfirmDialogData,
ConfirmDialogResult
>(ConfirmDialogComponent, {
width: '100%',
height: '500px',
disableClose: true,
data: {
title: '패스워드 변경',
html: `<iframe id="ifm_privacy" src="${privacyTotalUrl}" style="width: 100%;height: 300px;" />`
}
});
if (!result.choice) {
return throwError('privacy error');
}
return of(true);
}),
mergeMap(proceed => {
// 패스워드 변경
if (null === proceed) {
return of(null);
}
return this.piService.userTermsAction({
userSeq: loginRes.userSeq,
token: loginRes.tokenString,
deviceType: environmentsInfo.deviceType
});
}),
map(res => {
this.appAuthenticationService.login(
loginParams.loginInfo,
loginParams.rememberMe
);
this.store.dispatch(
loginSuccess({
loginInfo: loginRes
})
);
this.router.navigate(['/messenger']);
}),
catchError(error => {
return throwError(error);
})
);
}
),
catchError(error => {
this.store.dispatch(webLoginFailure({ error: error.message }));
return of(error);
})
),
{ dispatch: false }
); );
webLoginSuccess$ = createEffect( webLoginSuccess$ = createEffect(
@ -371,16 +128,10 @@ export class Effects {
constructor( constructor(
private actions$: Actions, private actions$: Actions,
private router: Router, private router: Router,
private store: Store<any>,
private piService: PiService, private piService: PiService,
private publicApiService: PublicApiService,
private protocolService: ProtocolService,
private innerProtocolService: InnerProtocolService,
private authenticationProtocolService: AuthenticationProtocolService,
private appAuthenticationService: AppAuthenticationService, private appAuthenticationService: AppAuthenticationService,
private dialogService: DialogService, private dialogService: DialogService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private sessionStorageService: SessionStorageService,
private logger: NGXLogger private logger: NGXLogger
) {} ) {}
} }

View File

@ -1,11 +1,10 @@
import { createAction, props } from '@ngrx/store'; import { createAction, props } from '@ngrx/store';
import { import {
BuddyRequest, BuddyRequest,
BuddyResponse,
BuddyDetailData,
GroupRequest, GroupRequest,
GroupDetailData, GroupDetailData,
GroupResponse GroupResponse,
UserInfo
} from '@ucap-webmessenger/protocol-sync'; } from '@ucap-webmessenger/protocol-sync';
export const buddy2 = createAction( export const buddy2 = createAction(
@ -13,14 +12,9 @@ export const buddy2 = createAction(
props<BuddyRequest>() props<BuddyRequest>()
); );
export const buddy2Data = createAction(
'[Messenger::Sync] Buddy2 Data',
props<{ data: BuddyDetailData }>()
);
export const buddy2Success = createAction( export const buddy2Success = createAction(
'[Messenger::Sync] Buddy2 Success', '[Messenger::Sync] Buddy2 Success',
props<{ res: BuddyResponse }>() props<{ buddyList: UserInfo[]; syncDate: string }>()
); );
export const buddy2Failure = createAction( export const buddy2Failure = createAction(
@ -33,14 +27,9 @@ export const group2 = createAction(
props<GroupRequest>() props<GroupRequest>()
); );
export const group2Data = createAction(
'[Messenger::Sync] Group2 Data',
props<{ data: GroupDetailData }>()
);
export const group2Success = createAction( export const group2Success = createAction(
'[Messenger::Sync] Group2 Success', '[Messenger::Sync] Group2 Success',
props<{ res: GroupResponse }>() props<{ groupList: GroupDetailData[]; syncDate: string }>()
); );
export const group2Failure = createAction( export const group2Failure = createAction(

View File

@ -3,7 +3,14 @@ import { Injectable } from '@angular/core';
import { Actions, ofType, createEffect } from '@ngrx/effects'; import { Actions, ofType, createEffect } from '@ngrx/effects';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { catchError, exhaustMap, map, withLatestFrom } from 'rxjs/operators'; import {
catchError,
exhaustMap,
map,
withLatestFrom,
switchMap,
tap
} from 'rxjs/operators';
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
@ -13,11 +20,9 @@ import {
buddy2, buddy2,
buddy2Success, buddy2Success,
buddy2Failure, buddy2Failure,
buddy2Data,
group2, group2,
group2Success, group2Success,
group2Failure, group2Failure
group2Data
} from './actions'; } from './actions';
import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { SessionStorageService } from '@ucap-webmessenger/web-storage';
@ -28,78 +33,81 @@ import {
BuddyDetailData, BuddyDetailData,
SSVC_TYPE_SYNC_GROUP_DATA2, SSVC_TYPE_SYNC_GROUP_DATA2,
GroupDetailData, GroupDetailData,
GroupResponse GroupResponse,
UserInfo,
SSVC_TYPE_SYNC_BUDDY2_RES,
SSVC_TYPE_SYNC_GROUP_RES2
} from '@ucap-webmessenger/protocol-sync'; } from '@ucap-webmessenger/protocol-sync';
import { regViewSuccess } from '@app/store/setting/option'; import { regViewSuccess } from '@app/store/setting/option';
@Injectable() @Injectable()
export class Effects { export class Effects {
regViewSuccess$ = createEffect(() => buddy2$ = createEffect(
this.actions$.pipe( () => {
ofType(regViewSuccess), let buddyList: UserInfo[];
withLatestFrom(
this.store.pipe( return this.actions$.pipe(
select(state => { ofType(buddy2),
this.logger.debug('state', state); tap(() => {
return state.messenger.sync.buddy2SyncDate as string; buddyList = [];
}) }),
) switchMap(req => {
), return this.syncProtocolService.buddy2(req).pipe(
map(([action, buddy2SyncDate]) => buddy2({ syncDate: buddy2SyncDate })) map(res => {
) switch (res.Type) {
case SSVC_TYPE_SYNC_BUDDY2_DATA:
buddyList.push(...(res as BuddyDetailData).buddyInfos);
break;
case SSVC_TYPE_SYNC_BUDDY2_RES:
this.store.dispatch(
buddy2Success({
buddyList,
syncDate: (res as BuddyResponse).syncDate
})
);
break;
}
}),
catchError(error => of(buddy2Failure({ error })))
);
})
);
},
{ dispatch: false }
); );
buddy2$ = createEffect(() => group2$ = createEffect(
this.actions$.pipe( () => {
ofType(buddy2), let groupList: GroupDetailData[];
exhaustMap(req =>
this.syncProtocolService.buddy2(req).pipe(
map(res => {
switch (res.Type) {
case SSVC_TYPE_SYNC_BUDDY2_DATA:
return buddy2Data({ data: res as BuddyDetailData });
}
return buddy2Success({ res: res as BuddyResponse }); return this.actions$.pipe(
}), ofType(group2),
catchError(error => of(buddy2Failure({ error }))) tap(() => {
) groupList = [];
) }),
) switchMap(req => {
); return this.syncProtocolService.group2(req).pipe(
map(res => {
buddy2SuccessToGroup2$ = createEffect(() => switch (res.Type) {
this.actions$.pipe( case SSVC_TYPE_SYNC_GROUP_DATA2:
ofType(buddy2Success), groupList.push(res as GroupDetailData);
withLatestFrom( break;
this.store.pipe( case SSVC_TYPE_SYNC_GROUP_RES2:
select(state => { this.store.dispatch(
this.logger.debug('state', state); group2Success({
return state.messenger.sync.group2SyncDate as string; groupList,
}) syncDate: (res as GroupResponse).syncDate
) })
), );
map(([action, group2SyncDate]) => group2({ syncDate: group2SyncDate })) break;
) }
); }),
catchError(error => of(group2Failure({ error })))
group2$ = createEffect(() => );
this.actions$.pipe( })
ofType(group2), );
exhaustMap(req => },
this.syncProtocolService.group2(req).pipe( { dispatch: false }
map(res => {
switch (res.Type) {
case SSVC_TYPE_SYNC_GROUP_DATA2:
return group2Data({ data: res as GroupDetailData });
}
return group2Success({ res: res as GroupResponse });
}),
catchError(error => of(group2Failure({ error })))
)
)
)
); );
constructor( constructor(

View File

@ -1,36 +1,22 @@
import { createReducer, on } from '@ngrx/store'; import { createReducer, on } from '@ngrx/store';
import { initialState } from './state'; import { initialState } from './state';
import { import { buddy2Success, group2Success } from './actions';
buddy2Data,
buddy2Success,
group2Data,
group2Success
} from './actions';
export const reducer = createReducer( export const reducer = createReducer(
initialState, initialState,
on(buddy2Data, (state, action) => {
return {
...state,
buddyInfoList: [...state.buddyInfoList, ...action.data.buddyInfos]
};
}),
on(buddy2Success, (state, action) => { on(buddy2Success, (state, action) => {
return { return {
...state, ...state,
buddy2SyncDate: action.res.syncDate buddyInfoList: action.buddyList,
}; buddy2SyncDate: action.syncDate
}),
on(group2Data, (state, action) => {
return {
...state,
groupList: [...state.groupList, action.data]
}; };
}), }),
on(group2Success, (state, action) => { on(group2Success, (state, action) => {
return { return {
...state, ...state,
group2SyncDate: action.res.syncDate groupList: action.groupList,
group2SyncDate: action.syncDate
}; };
}) })
); );

View File

@ -12,17 +12,17 @@ import { loginSuccess } from '@app/store/account/authentication';
@Injectable() @Injectable()
export class Effects { export class Effects {
initSettings$ = createEffect(() => // initSettings$ = createEffect(() =>
this.actions$.pipe( // this.actions$.pipe(
ofType(loginSuccess), // ofType(loginSuccess),
exhaustMap(() => // exhaustMap(() =>
this.optionProtocolService.regView({}).pipe( // this.optionProtocolService.regView({}).pipe(
map(res => regViewSuccess({ res })), // map(res => regViewSuccess({ res })),
catchError(error => of(regViewFailure({ error }))) // catchError(error => of(regViewFailure({ error })))
) // )
) // )
) // )
); // );
constructor( constructor(
private actions$: Actions, private actions$: Actions,

View File

@ -23,29 +23,29 @@ import { loginSuccess } from '@app/store/account/authentication';
@Injectable() @Injectable()
export class Effects { export class Effects {
initSettings$ = createEffect(() => // initSettings$ = createEffect(() =>
this.actions$.pipe( // this.actions$.pipe(
ofType(loginSuccess), // ofType(loginSuccess),
map(() => { // map(() => {
const loginInfo = this.sessionStorageService.get<LoginInfo>( // const loginInfo = this.sessionStorageService.get<LoginInfo>(
KEY_LOGIN_INFO // KEY_LOGIN_INFO
); // );
const environmentsInfo = this.sessionStorageService.get< // const environmentsInfo = this.sessionStorageService.get<
EnvironmentsInfo // EnvironmentsInfo
>(KEY_ENVIRONMENTS_INFO); // >(KEY_ENVIRONMENTS_INFO);
return { // return {
deviceType: environmentsInfo.deviceType // deviceType: environmentsInfo.deviceType
} as AuthRequest; // } as AuthRequest;
}), // }),
exhaustMap(req => // exhaustMap(req =>
this.queryProtocolService.auth(req).pipe( // this.queryProtocolService.auth(req).pipe(
map(res => authSuccess({ res })), // map(res => authSuccess({ res })),
catchError(error => of(authFailure({ error }))) // catchError(error => of(authFailure({ error })))
) // )
) // )
) // )
); // );
constructor( constructor(
private actions$: Actions, private actions$: Actions,

View File

@ -5,11 +5,40 @@
<mat-panel-description> </mat-panel-description> <mat-panel-description> </mat-panel-description>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<div <button
mat-button
class="contact"
*ngFor="let buddy of groupBuddy.buddyList" *ngFor="let buddy of groupBuddy.buddyList"
(click)="onClickBuddy(buddy)" (click)="onClickBuddy(buddy)"
[@animate]="{ value: '*', params: { y: '100%' } }"
> >
{{ buddy.name }} <div fxLayout="row" fxLayoutAlign="start center">
</div> <div
class="avatar-wrapper"
fxFlex="0 1 auto"
fxLayoutAlign="center center"
>
<img class="avatar" />
</div>
<div fxLayout="row" fxFlex>
<div
class="pr-4"
fxFlex
fxLayout="column"
fxLayoutAlign="center start"
>
<div class="contact-name">{{ buddy.name }}</div>
<div class="contact-last-message">{{ buddy.name }}</div>
</div>
<div fxLayout="column" fxLayoutAlign="start end">
<div class="contact-last-message-time">
{{ buddy.name }}
</div>
</div>
</div>
</div>
</button>
</mat-expansion-panel> </mat-expansion-panel>
</mat-accordion> </mat-accordion>

View File

@ -0,0 +1,52 @@
:host {
display: flex;
flex: 1;
flex-direction: column;
.contact {
white-space: normal;
text-align: left;
letter-spacing: 0.01em;
min-height: 88px;
border-bottom: 1px solid;
padding: 16px;
font-weight: 400;
border-radius: 0;
.avatar-wrapper {
.avatar {
margin-right: 16px;
}
}
.contact-name {
font-size: 16px;
white-space: nowrap;
text-overflow: ellipsis;
}
.contact-last-message {
line-height: normal;
margin-top: 0;
}
.contact-last-message-time {
font-size: 12px;
margin-top: 4px;
}
.contact-mood {
line-height: normal;
margin: 0;
}
.unread-message-count {
font-size: 12px;
border-radius: 50%;
text-align: center;
width: 20px;
height: 20px;
line-height: 20px;
}
}
}

View File

@ -1,11 +1,14 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { ucapAnimations } from '@ucap-webmessenger/ui';
import { GroupDetailData, UserInfo } from '@ucap-webmessenger/protocol-sync'; import { GroupDetailData, UserInfo } from '@ucap-webmessenger/protocol-sync';
@Component({ @Component({
selector: 'ucap-group-expansion-panel', selector: 'ucap-group-expansion-panel',
templateUrl: './expansion-panel.component.html', templateUrl: './expansion-panel.component.html',
styleUrls: ['./expansion-panel.component.scss'] styleUrls: ['./expansion-panel.component.scss'],
animations: ucapAnimations
}) })
export class ExpansionPanelComponent implements OnInit { export class ExpansionPanelComponent implements OnInit {
@Input() @Input()

View File

@ -0,0 +1,7 @@
import { Directive } from '@angular/core';
@Directive({
selector:
'ucap-group-expansion-panel-item, [ucap-group-expansion-panel-item], [ucapGroupExpansionPanelItem]'
})
export class ExpansionPanelItemDirective {}

View File

@ -2,17 +2,26 @@ import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatExpansionModule } from '@angular/material/expansion'; import { MatExpansionModule } from '@angular/material/expansion';
import { ExpansionPanelComponent } from './components/expansion-panel.component'; import { ExpansionPanelComponent } from './components/expansion-panel.component';
import { ExpansionPanelItemDirective } from './directives/expansion-panel-item.directive';
const COMPONENTS = [ExpansionPanelComponent]; const COMPONENTS = [ExpansionPanelComponent];
const DIRECTIVES = [ExpansionPanelItemDirective];
const SERVICES = []; const SERVICES = [];
@NgModule({ @NgModule({
imports: [CommonModule, ReactiveFormsModule, MatExpansionModule], imports: [
exports: [...COMPONENTS], CommonModule,
declarations: [...COMPONENTS] ReactiveFormsModule,
FlexLayoutModule,
MatExpansionModule
],
exports: [...COMPONENTS, ...DIRECTIVES],
declarations: [...COMPONENTS, ...DIRECTIVES]
}) })
export class UCapUiGroupModule { export class UCapUiGroupModule {
public static forRoot(): ModuleWithProviders<UCapUiGroupModule> { public static forRoot(): ModuleWithProviders<UCapUiGroupModule> {

View File

@ -1,6 +1,8 @@
import { NgModule, ModuleWithProviders } from '@angular/core'; import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
@ -11,7 +13,7 @@ const COMPONENTS = [ListItemComponent];
const SERVICES = []; const SERVICES = [];
@NgModule({ @NgModule({
imports: [CommonModule, MatButtonModule, MatIconModule], imports: [CommonModule, FlexLayoutModule, MatButtonModule, MatIconModule],
exports: [...COMPONENTS], exports: [...COMPONENTS],
declarations: [...COMPONENTS] declarations: [...COMPONENTS]
}) })

View File

@ -1,6 +1,16 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { EnviromentUtilService } from './enviroment-util.service'; import { EnviromentUtilService } from './enviroment-util.service';
import { interval, fromEvent, concat, merge, race, of, zip } from 'rxjs';
import {
take,
map,
combineAll,
tap,
mapTo,
delay,
switchMap
} from 'rxjs/operators';
describe('EnviromentUtilService', () => { describe('EnviromentUtilService', () => {
beforeEach(() => TestBed.configureTestingModule({})); beforeEach(() => TestBed.configureTestingModule({}));
@ -9,4 +19,87 @@ describe('EnviromentUtilService', () => {
const service: EnviromentUtilService = TestBed.get(EnviromentUtilService); const service: EnviromentUtilService = TestBed.get(EnviromentUtilService);
expect(service).toBeTruthy(); expect(service).toBeTruthy();
}); });
// it('rxjs combineAll', () => {
// const source = interval(1000).pipe(take(2));
// const example = source.pipe(
// map(sv => {
// console.log('sv', sv, new Date());
// return interval(1000).pipe(
// tap(ev => {
// console.log('sv', sv, 'ev', ev, new Date());
// }),
// map(ev => `Result (${sv}): ${ev}`),
// take(3)
// );
// })
// );
// const result = example.pipe(combineAll());
// result.subscribe(x => console.log('x', x, new Date()));
// });
// it('rxjs merge', () => {
// // emit every 2.5 seconds
// const first = interval(2500);
// // emit every 2 seconds
// const second = interval(2000);
// // emit every 1.5 seconds
// const third = interval(1500);
// // emit every 1 second
// const fourth = interval(1000);
// // emit outputs from one observable
// const example = merge(
// first.pipe(mapTo('FIRST!')),
// second.pipe(mapTo('SECOND!')),
// third.pipe(mapTo('THIRD')),
// fourth.pipe(mapTo('FOURTH'))
// );
// // output: "FOURTH", "THIRD", "SECOND!", "FOURTH", "FIRST!", "THIRD", "FOURTH"
// const subscribe = example.subscribe(val => console.log(val));
// });
// it('rxjs race', () => {
// // take the first observable to emit
// const example = race(
// // emit every 1.5s
// interval(1500).pipe(mapTo('1.5s won!')),
// // emit every 1s
// interval(1000).pipe(mapTo('1s won!')),
// // emit every 2s
// interval(2000).pipe(mapTo('2s won!')),
// // emit every 2.5s
// interval(2500).pipe(mapTo('2.5s won!'))
// );
// // output: "1s won!"..."1s won!"...etc
// const subscribe = example.subscribe(val => console.log(val));
// });
// it('rxjs zip', () => {
// const sourceOne = of('Hello');
// const sourceTwo = of('World!');
// const sourceThree = of('Goodbye');
// const sourceFour = of('World!');
// // wait until all observables have emitted a value then emit all as an array
// const example = zip(
// sourceOne,
// sourceTwo.pipe(delay(2000)),
// sourceThree.pipe(delay(1000)),
// sourceFour.pipe(delay(3000))
// );
// // output: ["Hello", "World!", "Goodbye", "World!"]
// const subscribe = example.subscribe(val => console.log(val));
// });
it('rxjs zip', () => {
const switched = of(1, 2, 3).pipe(
switchMap((x: number) => {
console.log('x', x);
return of(x, x ** 2, x ** 3);
})
);
switched.subscribe(x => console.log(x));
});
}); });