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

View File

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

View File

@ -4,17 +4,28 @@ import {
ActivatedRouteSnapshot,
RouterStateSnapshot
} from '@angular/router';
import { Observable, throwError } from 'rxjs';
import { map, tap, mergeMap, catchError, take } from 'rxjs/operators';
import { Observable, throwError, of, EMPTY, forkJoin } from 'rxjs';
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 { 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 {
LoginInfo,
KEY_LOGIN_INFO,
@ -29,6 +40,17 @@ import {
} from '@ucap-webmessenger/protocol-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()
export class AppMessengerResolver implements Resolve<void> {
@ -36,78 +58,115 @@ export class AppMessengerResolver implements Resolve<void> {
private store: Store<any>,
private sessionStorageService: SessionStorageService,
private publicApiService: PublicApiService,
private piService: PiService,
private protocolService: ProtocolService,
private queryProtocolService: QueryProtocolService,
private optionProtocolService: OptionProtocolService,
private authenticationProtocolService: AuthenticationProtocolService,
private innerProtocolService: InnerProtocolService
private innerProtocolService: InnerProtocolService,
private logger: NGXLogger
) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
activatedRouteSnapshot: ActivatedRouteSnapshot,
routerStateSnapshot: RouterStateSnapshot
): 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) => {
resolve();
// this.publicApiService
// .versionInfo2({
// deviceType: environmentsInfo.deviceType,
// companyGroupType: loginInfo.companyGroupType,
// companyCode: loginInfo.companyCode,
// loginId: loginInfo.loginId
// })
// .pipe(
// take(1),
// tap(versionInfo2Res => {
// this.store.dispatch(VersionInfoStore.fetchSuccess(versionInfo2Res));
// }),
// mergeMap(versionInfo2Res =>
// this.protocolService.connect(versionInfo2Res.serverIp)
// ),
// mergeMap(() => this.innerProtocolService.conn({})),
// mergeMap(connRres =>
// this.authenticationProtocolService.login({
// loginId: loginInfo.loginId,
// loginPw: loginInfo.loginPw,
// deviceType: environmentsInfo.deviceType,
// deviceId: ' ',
// token: '',
// localeCode: loginInfo.localeCode,
// pushId: ' ',
// companyCode: loginInfo.companyCode,
// passwordEncodingType: 1,
// clientVersion: '',
// reconnect: false,
// ip: 'localhost',
// hostName: '',
// ssoMode: SSOMode.AUTH,
// userSpecificInformation: 'PRO_000482',
// productId: 'PRO_000482',
// productName: 'EZMessenger'
// })
// ),
// map(loginRes => {
// this.store.dispatch(
// AuthenticationStore.loginSuccess({
// loginInfo: loginRes
// })
// );
// }),
// catchError(err => {
// return throwError(err);
// })
// )
// .subscribe(
// () => {
// resolve();
// },
// err => {
// reject();
// }
// );
let versionInfo2Res: VersionInfo2Response;
let connRres: ConnResponse;
let loginRes: LoginResponse;
const loginInfo = this.sessionStorageService.get<LoginInfo>(
KEY_LOGIN_INFO
);
const environmentsInfo = this.sessionStorageService.get<EnvironmentsInfo>(
KEY_ENVIRONMENTS_INFO
);
this.publicApiService
.versionInfo2({
deviceType: environmentsInfo.deviceType,
companyGroupType: loginInfo.companyGroupType,
companyCode: loginInfo.companyCode,
loginId: loginInfo.loginId
})
.pipe(
take(1),
switchMap(res => {
versionInfo2Res = res;
return this.protocolService.connect(versionInfo2Res.serverIp);
}),
switchMap(() => this.innerProtocolService.conn({})),
switchMap(res => {
connRres = res;
return this.authenticationProtocolService.login({
loginId: loginInfo.loginId,
loginPw: loginInfo.loginPw,
deviceType: environmentsInfo.deviceType,
deviceId: ' ',
token: '',
localeCode: loginInfo.localeCode,
pushId: ' ',
companyCode: loginInfo.companyCode,
passwordEncodingType: 1,
clientVersion: '',
reconnect: false,
ip: 'localhost',
hostName: '',
ssoMode: SSOMode.AUTH,
userSpecificInformation: 'PRO_000482',
productId: 'PRO_000482',
productName: 'EZMessenger'
});
}),
switchMap(res =>
forkJoin([
this.queryProtocolService.auth({
deviceType: environmentsInfo.deviceType
}),
this.optionProtocolService.regView({})
])
),
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 { Router } from '@angular/router';
import { of, throwError, EMPTY as empty } from 'rxjs';
import {
catchError,
exhaustMap,
map,
tap,
take,
switchMap,
mergeMap,
skip,
concatMap
} from 'rxjs/operators';
import { of } from 'rxjs';
import { catchError, exhaustMap, map, tap, switchMap } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions, ofType, createEffect } from '@ngrx/effects';
import {
PiService,
Login2Response,
ResponseStatus
} from '@ucap-webmessenger/pi';
import { PiService, Login2Response } from '@ucap-webmessenger/pi';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import {
DialogService,
@ -31,8 +16,6 @@ import {
} from '@ucap-webmessenger/ui';
import {
loginSuccess,
loginFailure,
loginRedirect,
logout,
logoutConfirmation,
@ -41,265 +24,39 @@ import {
webLoginSuccess,
webLoginFailure
} from './actions';
import {
LoginInfo,
EnvironmentsInfo,
KEY_ENVIRONMENTS_INFO
} from '../../../types';
import { LoginInfo } from '../../../types';
import { AppAuthenticationService } from '../../../services/authentication.service';
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()
export class Effects {
// webLogin$ = createEffect(() =>
// this.actions$.pipe(
// ofType(webLogin),
// map(action => action),
// exhaustMap((params: { loginInfo: LoginInfo; rememberMe: boolean }) =>
// this.piService
// .login2({
// loginId: params.loginInfo.loginId,
// loginPw: params.loginInfo.loginPw,
// companyCode: params.loginInfo.companyCode
// })
// .pipe(
// map((res: Login2Response) => {
// if ('success' !== res.status.toLowerCase()) {
// return webLoginFailure({ error: 'Failed' });
// } else {
// return webLoginSuccess({
// loginInfo: params.loginInfo,
// rememberMe: params.rememberMe,
// login2Response: res
// });
// }
// }),
// 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 }
webLogin$ = createEffect(() =>
this.actions$.pipe(
ofType(webLogin),
map(action => action),
exhaustMap((params: { loginInfo: LoginInfo; rememberMe: boolean }) =>
this.piService
.login2({
loginId: params.loginInfo.loginId,
loginPw: params.loginInfo.loginPw,
companyCode: params.loginInfo.companyCode
})
.pipe(
map((res: Login2Response) => {
if ('success' !== res.status.toLowerCase()) {
return webLoginFailure({ error: 'Failed' });
} else {
return webLoginSuccess({
loginInfo: params.loginInfo,
rememberMe: params.rememberMe,
login2Response: res
});
}
}),
catchError(error => of(webLoginFailure({ error })))
)
)
)
);
webLoginSuccess$ = createEffect(
@ -371,16 +128,10 @@ export class Effects {
constructor(
private actions$: Actions,
private router: Router,
private store: Store<any>,
private piService: PiService,
private publicApiService: PublicApiService,
private protocolService: ProtocolService,
private innerProtocolService: InnerProtocolService,
private authenticationProtocolService: AuthenticationProtocolService,
private appAuthenticationService: AppAuthenticationService,
private dialogService: DialogService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private sessionStorageService: SessionStorageService,
private logger: NGXLogger
) {}
}

View File

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

View File

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

View File

@ -1,36 +1,22 @@
import { createReducer, on } from '@ngrx/store';
import { initialState } from './state';
import {
buddy2Data,
buddy2Success,
group2Data,
group2Success
} from './actions';
import { buddy2Success, group2Success } from './actions';
export const reducer = createReducer(
initialState,
on(buddy2Data, (state, action) => {
return {
...state,
buddyInfoList: [...state.buddyInfoList, ...action.data.buddyInfos]
};
}),
on(buddy2Success, (state, action) => {
return {
...state,
buddy2SyncDate: action.res.syncDate
};
}),
on(group2Data, (state, action) => {
return {
...state,
groupList: [...state.groupList, action.data]
buddyInfoList: action.buddyList,
buddy2SyncDate: action.syncDate
};
}),
on(group2Success, (state, action) => {
return {
...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()
export class Effects {
initSettings$ = createEffect(() =>
this.actions$.pipe(
ofType(loginSuccess),
exhaustMap(() =>
this.optionProtocolService.regView({}).pipe(
map(res => regViewSuccess({ res })),
catchError(error => of(regViewFailure({ error })))
)
)
)
);
// initSettings$ = createEffect(() =>
// this.actions$.pipe(
// ofType(loginSuccess),
// exhaustMap(() =>
// this.optionProtocolService.regView({}).pipe(
// map(res => regViewSuccess({ res })),
// catchError(error => of(regViewFailure({ error })))
// )
// )
// )
// );
constructor(
private actions$: Actions,

View File

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

View File

@ -5,11 +5,40 @@
<mat-panel-description> </mat-panel-description>
</mat-expansion-panel-header>
<div
<button
mat-button
class="contact"
*ngFor="let buddy of groupBuddy.buddyList"
(click)="onClickBuddy(buddy)"
[@animate]="{ value: '*', params: { y: '100%' } }"
>
{{ buddy.name }}
</div>
<div fxLayout="row" fxLayoutAlign="start center">
<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-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 { ucapAnimations } from '@ucap-webmessenger/ui';
import { GroupDetailData, UserInfo } from '@ucap-webmessenger/protocol-sync';
@Component({
selector: 'ucap-group-expansion-panel',
templateUrl: './expansion-panel.component.html',
styleUrls: ['./expansion-panel.component.scss']
styleUrls: ['./expansion-panel.component.scss'],
animations: ucapAnimations
})
export class ExpansionPanelComponent implements OnInit {
@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 { ReactiveFormsModule } from '@angular/forms';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatExpansionModule } from '@angular/material/expansion';
import { ExpansionPanelComponent } from './components/expansion-panel.component';
import { ExpansionPanelItemDirective } from './directives/expansion-panel-item.directive';
const COMPONENTS = [ExpansionPanelComponent];
const DIRECTIVES = [ExpansionPanelItemDirective];
const SERVICES = [];
@NgModule({
imports: [CommonModule, ReactiveFormsModule, MatExpansionModule],
exports: [...COMPONENTS],
declarations: [...COMPONENTS]
imports: [
CommonModule,
ReactiveFormsModule,
FlexLayoutModule,
MatExpansionModule
],
exports: [...COMPONENTS, ...DIRECTIVES],
declarations: [...COMPONENTS, ...DIRECTIVES]
})
export class UCapUiGroupModule {
public static forRoot(): ModuleWithProviders<UCapUiGroupModule> {

View File

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

View File

@ -1,6 +1,16 @@
import { TestBed } from '@angular/core/testing';
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', () => {
beforeEach(() => TestBed.configureTestingModule({}));
@ -9,4 +19,87 @@ describe('EnviromentUtilService', () => {
const service: EnviromentUtilService = TestBed.get(EnviromentUtilService);
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));
});
});