This commit is contained in:
병준 박 2019-10-29 18:19:51 +09:00
commit 9a13fbde18
30 changed files with 616 additions and 136 deletions

View File

@ -32,6 +32,7 @@
</ng-template> </ng-template>
<app-layout-chat-left-sidenav-organization <app-layout-chat-left-sidenav-organization
[selectedUserList]="selectedUserList" [selectedUserList]="selectedUserList"
(checkAllUser)="onCheckAllUser($event)"
(checkUser)="onCheckUser($event)" (checkUser)="onCheckUser($event)"
></app-layout-chat-left-sidenav-organization> ></app-layout-chat-left-sidenav-organization>
</mat-tab> </mat-tab>

View File

@ -67,6 +67,26 @@ export class LeftSideComponent implements OnInit {
} }
} }
onCheckAllUser(params: {
isChecked: boolean;
userInfos: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[];
}) {
params.userInfos.forEach(userInfo => {
if (params.isChecked) {
if (
this.selectedUserList.filter(user => user.seq === userInfo.seq)
.length === 0
) {
this.selectedUserList = [...this.selectedUserList, userInfo];
}
} else {
this.selectedUserList = this.selectedUserList.filter(
user => user.seq !== userInfo.seq
);
}
});
}
/** 조직도>부서원 :: 리스트의 checkbox 의 이벤트를 받아 선택된 유저리스트를 수집. */ /** 조직도>부서원 :: 리스트의 checkbox 의 이벤트를 받아 선택된 유저리스트를 수집. */
onCheckUser(params: { onCheckUser(params: {
isChecked: boolean; isChecked: boolean;
@ -74,15 +94,15 @@ export class LeftSideComponent implements OnInit {
}) { }) {
if (params.isChecked) { if (params.isChecked) {
if ( if (
params.userInfo &&
this.selectedUserList.filter(user => user.seq === params.userInfo.seq) this.selectedUserList.filter(user => user.seq === params.userInfo.seq)
.length === 0 && .length === 0
params.userInfo
) { ) {
this.selectedUserList = [...this.selectedUserList, params.userInfo]; this.selectedUserList = [...this.selectedUserList, params.userInfo];
} }
} else { } else {
this.selectedUserList = this.selectedUserList.filter( this.selectedUserList = this.selectedUserList.filter(
item => item.seq !== params.userInfo.seq user => user.seq !== params.userInfo.seq
); );
} }
} }

View File

@ -11,7 +11,20 @@
</div> </div>
<div fxFlex="60"> <div fxFlex="60">
<div class="select-dept"> <div class="select-dept">
{{ getSelectedDepartmentName() }} <dl>
<dt>
{{ getSelectedDepartmentName() }}
</dt>
<dd>
<mat-checkbox
#checkbox
[checked]="getCheckedAllUser()"
(change)="onCheckAllUser(checkbox.checked)"
(click)="$event.stopPropagation()"
>
</mat-checkbox>
</dd>
</dl>
</div> </div>
<div *ngIf="selectedDepartmentProcessing"> <div *ngIf="selectedDepartmentProcessing">
<mat-progress-bar mode="indeterminate"></mat-progress-bar> <mat-progress-bar mode="indeterminate"></mat-progress-bar>
@ -22,9 +35,7 @@
style="height: calc(100% - 20px);" style="height: calc(100% - 20px);"
> >
<ucap-profile-user-list-item <ucap-profile-user-list-item
*cdkVirtualFor=" *cdkVirtualFor="let userInfo of selectedDepartmentUserInfoList"
let userInfo of selectedDepartmentUserInfoList$ | async
"
[userInfo]="userInfo" [userInfo]="userInfo"
[checkable]="true" [checkable]="true"
[sessionVerinfo]="sessionVerinfo" [sessionVerinfo]="sessionVerinfo"

View File

@ -55,9 +55,16 @@ export class OrganizationComponent implements OnInit, OnDestroy {
isChecked: boolean; isChecked: boolean;
userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN; userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN;
}>(); }>();
@Output()
checkAllUser = new EventEmitter<{
isChecked: boolean;
userInfos: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[];
}>();
departmentInfoList$: Observable<DeptInfo[]>; departmentInfoList$: Observable<DeptInfo[]>;
selectedDepartmentUserInfoList$: Observable<UserInfoSS[]>; selectedDepartmentUserInfoList$: Observable<UserInfoSS[]>;
selectedDepartmentUserInfoList: UserInfoSS[] = [];
selectedDepartmentUserInfoListSubscription: Subscription;
selectedDepartmentStatus$: Observable<DeptUserResponse>; selectedDepartmentStatus$: Observable<DeptUserResponse>;
selectedDepartmentProcessing = false; selectedDepartmentProcessing = false;
selectedDepartmentProcessingSubscription: Subscription; selectedDepartmentProcessingSubscription: Subscription;
@ -68,7 +75,6 @@ export class OrganizationComponent implements OnInit, OnDestroy {
constructor( constructor(
private store: Store<any>, private store: Store<any>,
private queryProtocolService: QueryProtocolService,
private sessionStorageService: SessionStorageService, private sessionStorageService: SessionStorageService,
private dialogService: DialogService, private dialogService: DialogService,
private logger: NGXLogger private logger: NGXLogger
@ -96,11 +102,18 @@ export class OrganizationComponent implements OnInit, OnDestroy {
) )
.subscribe(); .subscribe();
this.selectedDepartmentUserInfoList$ = this.store.pipe( this.selectedDepartmentUserInfoListSubscription = this.store
select( .pipe(
AppStore.MessengerSelector.QuerySelector.selectedDepartmentUserInfoList select(
AppStore.MessengerSelector.QuerySelector
.selectedDepartmentUserInfoList
),
map(list => {
this.selectedDepartmentUserInfoList = list;
})
) )
); .subscribe();
this.selectedDepartmentStatus$ = this.store.pipe( this.selectedDepartmentStatus$ = this.store.pipe(
select(AppStore.MessengerSelector.QuerySelector.selectedDepartmentStatus) select(AppStore.MessengerSelector.QuerySelector.selectedDepartmentStatus)
); );
@ -131,6 +144,9 @@ export class OrganizationComponent implements OnInit, OnDestroy {
} }
ngOnDestroy(): void { ngOnDestroy(): void {
if (!!this.selectedDepartmentUserInfoListSubscription) {
this.selectedDepartmentUserInfoListSubscription.unsubscribe();
}
if (!!this.selectedDepartmentProcessingSubscription) { if (!!this.selectedDepartmentProcessingSubscription) {
this.selectedDepartmentProcessingSubscription.unsubscribe(); this.selectedDepartmentProcessingSubscription.unsubscribe();
} }
@ -173,6 +189,23 @@ export class OrganizationComponent implements OnInit, OnDestroy {
} }
} }
/** 전체 체크여부 */
getCheckedAllUser() {
if (
this.selectedDepartmentUserInfoList &&
this.selectedDepartmentUserInfoList.filter(
item =>
!(
this.selectedUserList.filter(user => user.seq === item.seq).length >
0
)
).length > 0
) {
return false;
} else {
return true;
}
}
/** 리스트 checkable 할 경우 checkbox 의 isChecked 를 관장하며 리스트의 전체선택 여부를 판단한다. */ /** 리스트 checkable 할 경우 checkbox 의 isChecked 를 관장하며 리스트의 전체선택 여부를 판단한다. */
getCheckedUser(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) { getCheckedUser(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) {
if (!!this.selectedUserList && this.selectedUserList.length > 0) { if (!!this.selectedUserList && this.selectedUserList.length > 0) {
@ -184,6 +217,14 @@ export class OrganizationComponent implements OnInit, OnDestroy {
return false; return false;
} }
/** 전체선택 이벤트 */
onCheckAllUser(value: boolean) {
this.checkAllUser.emit({
isChecked: value,
userInfos: this.selectedDepartmentUserInfoList
});
}
/** 리스트가 checkable 할 경우 checkbox 의 change 이벤트를 상위 컴포넌트로 전달한다. */ /** 리스트가 checkable 할 경우 checkbox 의 change 이벤트를 상위 컴포넌트로 전달한다. */
onCheckUser(params: { onCheckUser(params: {
isChecked: boolean; isChecked: boolean;

View File

@ -73,6 +73,7 @@
<perfect-scrollbar fxFlex="1 1 auto" #psChatContent> <perfect-scrollbar fxFlex="1 1 auto" #psChatContent>
<ucap-chat-messages <ucap-chat-messages
[messages]="eventList$ | async" [messages]="eventList$ | async"
[eventInfoStatus]="eventInfoStatus$ | async"
[userInfos]="userInfoList" [userInfos]="userInfoList"
[loginRes]="loginRes" [loginRes]="loginRes"
[sessionVerInfo]="sessionVerInfo" [sessionVerInfo]="sessionVerInfo"

View File

@ -23,7 +23,8 @@ import {
EventType, EventType,
isRecalled, isRecalled,
isCopyable, isCopyable,
isRecallable isRecallable,
InfoResponse
} from '@ucap-webmessenger/protocol-event'; } from '@ucap-webmessenger/protocol-event';
import * as AppStore from '@app/store'; import * as AppStore from '@app/store';
@ -84,6 +85,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
userInfoList: UserInfo[]; userInfoList: UserInfo[];
userInfoListSubscription: Subscription; userInfoListSubscription: Subscription;
eventListProcessing$: Observable<boolean>; eventListProcessing$: Observable<boolean>;
eventInfoStatus$: Observable<InfoResponse>;
sessionVerInfo: VersionInfo2Response; sessionVerInfo: VersionInfo2Response;
isRecalledMessage = isRecalled; isRecalledMessage = isRecalled;
@ -147,6 +149,10 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
select(AppStore.MessengerSelector.EventSelector.selectAllInfoList) select(AppStore.MessengerSelector.EventSelector.selectAllInfoList)
); );
this.eventInfoStatus$ = this.store.pipe(
select(AppStore.MessengerSelector.EventSelector.infoStatus)
);
this.psChatContent.directiveRef.scrollToBottom(0, 0); this.psChatContent.directiveRef.scrollToBottom(0, 0);
} }

View File

@ -91,6 +91,7 @@
<app-layout-chat-left-sidenav-organization <app-layout-chat-left-sidenav-organization
[selectedUserList]="selectedUserList" [selectedUserList]="selectedUserList"
[isUserSelect]="true" [isUserSelect]="true"
(checkAllUser)="onCheckAllUser($event)"
(checkUser)="onCheckUser($event)" (checkUser)="onCheckUser($event)"
> >
</app-layout-chat-left-sidenav-organization> </app-layout-chat-left-sidenav-organization>

View File

@ -330,6 +330,26 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy {
} }
} }
onCheckAllUser(params: {
isChecked: boolean;
userInfos: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[];
}) {
params.userInfos.forEach(userInfo => {
if (params.isChecked) {
if (
this.selectedUserList.filter(user => user.seq === userInfo.seq)
.length === 0
) {
this.selectedUserList = [...this.selectedUserList, userInfo];
}
} else {
this.selectedUserList = this.selectedUserList.filter(
user => user.seq !== userInfo.seq
);
}
});
}
/** 동료그룹>부서원, 조직도>부서원 :: 리스트의 checkbox 의 이벤트를 받아 선택된 유저리스트를 수집. */ /** 동료그룹>부서원, 조직도>부서원 :: 리스트의 checkbox 의 이벤트를 받아 선택된 유저리스트를 수집. */
onCheckUser(params: { onCheckUser(params: {
isChecked: boolean; isChecked: boolean;

View File

@ -21,6 +21,7 @@ import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatCheckboxModule } from '@angular/material';
import { DragDropModule } from '@angular/cdk/drag-drop'; import { DragDropModule } from '@angular/cdk/drag-drop';
import { OverlayModule } from '@angular/cdk/overlay'; import { OverlayModule } from '@angular/cdk/overlay';
@ -63,6 +64,7 @@ import { DIALOGS } from './dialogs';
MatTabsModule, MatTabsModule,
MatToolbarModule, MatToolbarModule,
MatChipsModule, MatChipsModule,
MatCheckboxModule,
PerfectScrollbarModule, PerfectScrollbarModule,

View File

@ -1,8 +1,9 @@
import { delGroupSuccess, buddy2 } from './../store/messenger/sync/actions';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { tap } from 'rxjs/operators'; import { tap, withLatestFrom } from 'rxjs/operators';
import { Store } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
import { import {
SSVC_TYPE_LOGOUT_RES, SSVC_TYPE_LOGOUT_RES,
@ -35,7 +36,7 @@ import {
SSVC_TYPE_ROOM_EXIT_FORCING_NOTI, SSVC_TYPE_ROOM_EXIT_FORCING_NOTI,
SSVC_TYPE_ROOM_FONT_UPD_NOTI, SSVC_TYPE_ROOM_FONT_UPD_NOTI,
InviteNotification, InviteNotification,
UpdateNotification, UpdateNotification as RoomUpdateNotification,
SSVC_TYPE_ROOM_UPD_RES SSVC_TYPE_ROOM_UPD_RES
} from '@ucap-webmessenger/protocol-room'; } from '@ucap-webmessenger/protocol-room';
import { import {
@ -53,10 +54,29 @@ import {
ExitForcingNotification, ExitForcingNotification,
UpdateFontNotification UpdateFontNotification
} from '@ucap-webmessenger/protocol-room'; } from '@ucap-webmessenger/protocol-room';
import {
GroupProtocolService,
SSVC_TYPE_GROUP_UPD_RES2,
UpdateNotification as GroupUpdateNotification,
SSVC_TYPE_GROUP_ADD_RES,
AddNotification as GroupAddNotification,
SSVC_TYPE_GROUP_DEL_RES,
DelNotification as GroupDelNotification
} from '@ucap-webmessenger/protocol-group';
import {
BuddyProtocolService,
SSVC_TYPE_BUDDY_UPD_RES,
UpdateNotification as BuddyUpdateNotification,
SSVC_TYPE_BUDDY_ADD_RES,
AddNotification as BuddyAddNotification,
SSVC_TYPE_BUDDY_DEL_RES,
DelNotification as BuddyDelNotification
} from '@ucap-webmessenger/protocol-buddy';
import * as AuthenticationStore from '@app/store/account/authentication'; import * as AuthenticationStore from '@app/store/account/authentication';
import * as InfoStore from '@app/store/account/info'; import * as InfoStore from '@app/store/account/info';
import * as EventStore from '@app/store/messenger/event'; import * as EventStore from '@app/store/messenger/event';
import * as SyncStore from '@app/store/messenger/sync';
import * as RoomStore from '@app/store/messenger/room'; import * as RoomStore from '@app/store/messenger/room';
import * as StatusStore from '@app/store/messenger/status'; import * as StatusStore from '@app/store/messenger/status';
@ -67,6 +87,8 @@ export class AppNotificationService {
private eventProtocolService: EventProtocolService, private eventProtocolService: EventProtocolService,
private infoProtocolService: InfoProtocolService, private infoProtocolService: InfoProtocolService,
private roomProtocolService: RoomProtocolService, private roomProtocolService: RoomProtocolService,
private groupProtocolService: GroupProtocolService,
private buddyProtocolService: BuddyProtocolService,
private statusProtocolService: StatusProtocolService, private statusProtocolService: StatusProtocolService,
private store: Store<any>, private store: Store<any>,
private logger: NGXLogger private logger: NGXLogger
@ -197,15 +219,115 @@ export class AppNotificationService {
}) })
) )
.subscribe(); .subscribe();
this.groupProtocolService.notification$
.pipe(
withLatestFrom(
this.store.pipe(
select(
(state: any) => state.messenger.sync.group2.syncDate as string
)
)
),
tap(([notiOrRes, syncDate]) => {
switch (notiOrRes.SSVC_TYPE) {
case SSVC_TYPE_GROUP_UPD_RES2:
{
const noti = notiOrRes as GroupUpdateNotification;
this.logger.debug(
'Notification::groupProtocolService::GroupUpdateNotification',
noti
);
this.store.dispatch(
SyncStore.group2({
syncDate
})
);
}
break;
case SSVC_TYPE_GROUP_ADD_RES:
{
const noti = notiOrRes as GroupAddNotification;
this.logger.debug(
'Notification::groupProtocolService::GroupAddNotification',
noti
);
this.store.dispatch(SyncStore.createGroupSuccess(noti));
}
break;
case SSVC_TYPE_GROUP_DEL_RES:
{
const noti = notiOrRes as GroupDelNotification;
this.logger.debug(
'Notification::groupProtocolService::GroupDelNotification',
noti
);
this.store.dispatch(SyncStore.delGroupSuccess(noti));
}
break;
default:
break;
}
})
)
.subscribe();
this.buddyProtocolService.notification$
.pipe(
withLatestFrom(
this.store.pipe(
select(
(state: any) => state.messenger.sync.buddy2.syncDate as string
)
)
),
tap(([notiOrRes, syncDate]) => {
switch (notiOrRes.SSVC_TYPE) {
case SSVC_TYPE_BUDDY_UPD_RES:
{
const noti = notiOrRes as BuddyUpdateNotification;
this.logger.debug(
'Notification::groupProtocolService::BuddyUpdateNotification',
noti
);
this.store.dispatch(SyncStore.updateBuddySuccess(noti));
}
break;
case SSVC_TYPE_BUDDY_ADD_RES:
{
const noti = notiOrRes as BuddyAddNotification;
this.logger.debug(
'Notification::groupProtocolService::BuddyAddNotification',
noti
);
this.store.dispatch(SyncStore.buddy2({ syncDate }));
}
break;
case SSVC_TYPE_BUDDY_DEL_RES:
{
const noti = notiOrRes as BuddyDelNotification;
this.logger.debug(
'Notification::groupProtocolService::BuddyDelNotification',
noti
);
this.store.dispatch(SyncStore.delBuddySuccess(noti));
}
break;
default:
break;
}
})
)
.subscribe();
this.roomProtocolService.notification$ this.roomProtocolService.notification$
.pipe( .pipe(
tap(notiOrRes => { tap(notiOrRes => {
switch (notiOrRes.SSVC_TYPE) { switch (notiOrRes.SSVC_TYPE) {
case SSVC_TYPE_ROOM_UPD_RES: case SSVC_TYPE_ROOM_UPD_RES:
{ {
const noti = notiOrRes as UpdateNotification; const noti = notiOrRes as RoomUpdateNotification;
this.logger.debug( this.logger.debug(
'Notification::roomProtocolService::UpdateNotification', 'Notification::roomProtocolService::RoomUpdateNotification',
noti noti
); );
this.store.dispatch( this.store.dispatch(

View File

@ -571,73 +571,68 @@ export class Effects {
{ dispatch: false } { dispatch: false }
); );
delGroup$ = createEffect( delGroup$ = createEffect(() =>
() => { this.actions$.pipe(
return this.actions$.pipe( ofType(delGroup),
ofType(delGroup), withLatestFrom(
withLatestFrom( this.store.pipe(
this.store.pipe( select(
select( (state: any) =>
(state: any) => state.messenger.sync.group2.entities as Dictionary<
state.messenger.sync.group2.entities as Dictionary< GroupDetailData
GroupDetailData >
>
)
) )
), )
map(([action, groupList]) => { ),
// Del Buddy exhaustMap(([action, groupList]) => {
const trgtBuddys = action.group.userSeqs; // Del Buddy
// tslint:disable-next-line: no-shadowed-variable const trgtBuddys = action.group.userSeqs;
const delBuddyList = trgtBuddys.filter(delBuddy => { // tslint:disable-next-line: no-shadowed-variable
let exist = false; const delBuddyList = trgtBuddys.filter(delBuddy => {
// tslint:disable-next-line: forin let exist = false;
for (const key in groupList) { // tslint:disable-next-line: forin
const group: GroupDetailData = groupList[key]; for (const key in groupList) {
if ( const group: GroupDetailData = groupList[key];
group.seq !== action.group.seq && if (
group.userSeqs.filter(v => v === delBuddy).length > 0 group.seq !== action.group.seq &&
) { group.userSeqs.filter(v => v === delBuddy).length > 0
exist = true; ) {
break; exist = true;
} break;
} }
return !exist; }
return !exist;
});
if (delBuddyList.length > 0) {
this.logger.debug('Del Buddy', delBuddyList);
// 즐겨찾기 해제.
delBuddyList.forEach(buddySeq => {
this.buddyProtocolService
.update({
seq: buddySeq,
isFavorit: false
})
.pipe(catchError(error => of(delBuddyFailure({ error }))));
}); });
if (delBuddyList.length > 0) { // 동료 삭제
this.logger.debug('Del Buddy', delBuddyList); this.store.dispatch(delBuddy({ userSeqs: delBuddyList }));
// 즐겨찾기 해제. }
delBuddyList.forEach(buddySeq => {
this.buddyProtocolService
.update({
seq: buddySeq,
isFavorit: false
})
.pipe(catchError(error => of(delBuddyFailure({ error }))));
});
// 동료 삭제 return this.groupProtocolService
this.store.dispatch(delBuddy({ userSeqs: delBuddyList })); .del({
} groupSeq: action.group.seq
})
return action.group; .pipe(
}), map((res: GroupDelResponse) => {
tap(group => { // this.store.dispatch(delGroupSuccess(res));
this.groupProtocolService return delGroupSuccess(res);
.del({ }),
groupSeq: group.seq catchError(error => of(delGroupFailure({ error })))
}) );
.pipe( })
map((res: GroupDelResponse) => { )
return delGroupSuccess(res);
}),
catchError(error => of(delGroupFailure({ error })))
);
})
);
},
{ dispatch: false }
); );
addBuddy$ = createEffect(() => addBuddy$ = createEffect(() =>
@ -674,11 +669,6 @@ export class Effects {
map((res: BuddyDelResponse) => { map((res: BuddyDelResponse) => {
return delBuddySuccess(res); return delBuddySuccess(res);
}), }),
// map((res: BuddyDelResponse) => {
// return buddy2({
// syncDate
// });
// }),
catchError(error => of(delBuddyFailure({ error }))) catchError(error => of(delBuddyFailure({ error })))
) )
) )

View File

@ -7,16 +7,22 @@ import {
PacketBodyValue, PacketBodyValue,
ProtocolDecoder, ProtocolDecoder,
ProtocolMessage, ProtocolMessage,
decodeProtocolMessage decodeProtocolMessage,
ProtocolNotification
} from '@ucap-webmessenger/protocol'; } from '@ucap-webmessenger/protocol';
export interface AddRequest extends ProtocolRequest { export interface AddRequest extends ProtocolRequest {
// 0n. 사용자SEQ(n)... /** 0n. 사용자SEQ(n)... */
userSeqs: number[]; userSeqs: number[];
} }
export interface AddResponse extends ProtocolResponse { export interface AddResponse extends ProtocolResponse {
// 0n. 사용자SEQ(n)... /** 0n. 사용자SEQ(n)... */
userSeqs: number[];
}
export interface AddNotification extends ProtocolNotification {
/** 0n. 사용자SEQ(n)... */
userSeqs: number[]; userSeqs: number[];
} }
@ -38,3 +44,12 @@ export const decodeAdd: ProtocolDecoder<AddResponse> = (
userSeqs: userSeqArray userSeqs: userSeqArray
} as AddResponse); } as AddResponse);
}; };
export const decodeAddNotification: ProtocolDecoder<AddNotification> = (
message: ProtocolMessage
) => {
const userSeqArray: number[] = [...message.bodyList];
return decodeProtocolMessage(message, {
userSeqs: userSeqArray
} as AddNotification);
};

View File

@ -7,16 +7,22 @@ import {
PacketBodyValue, PacketBodyValue,
ProtocolDecoder, ProtocolDecoder,
ProtocolMessage, ProtocolMessage,
decodeProtocolMessage decodeProtocolMessage,
ProtocolNotification
} from '@ucap-webmessenger/protocol'; } from '@ucap-webmessenger/protocol';
export interface DelRequest extends ProtocolRequest { export interface DelRequest extends ProtocolRequest {
// 0n. 사용자SEQ(n)... /** 0n. 사용자SEQ(n)... */
userSeqs: number[]; userSeqs: number[];
} }
export interface DelResponse extends ProtocolResponse { export interface DelResponse extends ProtocolResponse {
// 0n. 사용자SEQ(n)... /** 0n. 사용자SEQ(n)... */
userSeqs: number[];
}
export interface DelNotification extends ProtocolNotification {
/** 0n. 사용자SEQ(n)... */
userSeqs: number[]; userSeqs: number[];
} }
@ -38,3 +44,12 @@ export const decodeDel: ProtocolDecoder<DelResponse> = (
userSeqs: userSeqArray userSeqs: userSeqArray
} as DelResponse); } as DelResponse);
}; };
export const decodeDelNotification: ProtocolDecoder<DelNotification> = (
message: ProtocolMessage
) => {
const userSeqArray: number[] = [...message.bodyList];
return decodeProtocolMessage(message, {
userSeqs: userSeqArray
} as DelNotification);
};

View File

@ -7,7 +7,8 @@ import {
PacketBodyValue, PacketBodyValue,
ProtocolDecoder, ProtocolDecoder,
ProtocolMessage, ProtocolMessage,
decodeProtocolMessage decodeProtocolMessage,
ProtocolNotification
} from '@ucap-webmessenger/protocol'; } from '@ucap-webmessenger/protocol';
export interface UpdateRequest extends ProtocolRequest { export interface UpdateRequest extends ProtocolRequest {
@ -24,6 +25,13 @@ export interface UpdateResponse extends ProtocolResponse {
isFavorit: boolean; isFavorit: boolean;
} }
export interface UpdateNotification extends ProtocolNotification {
// 0. 사용자SEQ(n)
seq: number;
// 1. 즐겨찾기여부(y)
isFavorit: boolean;
}
export const encodeUpdate: ProtocolEncoder<UpdateRequest> = ( export const encodeUpdate: ProtocolEncoder<UpdateRequest> = (
req: UpdateRequest req: UpdateRequest
) => { ) => {
@ -46,3 +54,12 @@ export const decodeUpdate: ProtocolDecoder<UpdateResponse> = (
isFavorit: message.bodyList[1] === 'Y' ? true : false isFavorit: message.bodyList[1] === 'Y' ? true : false
} as UpdateResponse); } as UpdateResponse);
}; };
export const decodeUpdateNotification: ProtocolDecoder<UpdateNotification> = (
message: ProtocolMessage
) => {
return decodeProtocolMessage(message, {
seq: message.bodyList[0],
isFavorit: message.bodyList[1] === 'Y' ? true : false
} as UpdateNotification);
};

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { map, take } from 'rxjs/operators'; import { map, take, share, filter, tap } from 'rxjs/operators';
import { ProtocolService } from '@ucap-webmessenger/protocol'; import { ProtocolService } from '@ucap-webmessenger/protocol';
@ -9,33 +9,80 @@ import {
SVC_TYPE_BUDDY, SVC_TYPE_BUDDY,
SSVC_TYPE_BUDDY_ADD_REQ, SSVC_TYPE_BUDDY_ADD_REQ,
SSVC_TYPE_BUDDY_DEL_REQ, SSVC_TYPE_BUDDY_DEL_REQ,
SSVC_TYPE_BUDDY_UPD_REQ SSVC_TYPE_BUDDY_UPD_REQ,
SSVC_TYPE_BUDDY_UPD_RES,
SSVC_TYPE_BUDDY_ADD_RES,
SSVC_TYPE_BUDDY_DEL_RES
} from '../types/service'; } from '../types/service';
import { import {
AddRequest, AddRequest,
encodeAdd, encodeAdd,
decodeAdd, decodeAdd,
AddResponse AddResponse,
decodeAddNotification,
AddNotification
} from '../protocols/add'; } from '../protocols/add';
import { import {
DelRequest, DelRequest,
encodeDel, encodeDel,
decodeDel, decodeDel,
DelResponse DelResponse,
decodeDelNotification,
DelNotification
} from '../protocols/del'; } from '../protocols/del';
import { import {
UpdateRequest, UpdateRequest,
decodeUpdate, decodeUpdate,
encodeUpdate, encodeUpdate,
UpdateResponse UpdateResponse,
UpdateNotification,
decodeUpdateNotification
} from '../protocols/update'; } from '../protocols/update';
type Notifications = UpdateNotification | AddNotification | DelNotification;
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class BuddyProtocolService { export class BuddyProtocolService {
constructor(private protocolService: ProtocolService) {} private notificationSubject: Subject<Notifications>;
public notification$: Observable<Notifications>;
constructor(private protocolService: ProtocolService) {
this.notificationSubject = new Subject();
this.notification$ = this.notificationSubject.asObservable().pipe(share());
this.protocolService.serverMessage
.pipe(
filter(message => message.serviceType === SVC_TYPE_BUDDY),
tap(message => {
switch (message.subServiceType) {
case SSVC_TYPE_BUDDY_UPD_RES:
{
this.notificationSubject.next(
decodeUpdateNotification(message)
);
}
break;
case SSVC_TYPE_BUDDY_ADD_RES:
{
this.notificationSubject.next(decodeAddNotification(message));
}
break;
case SSVC_TYPE_BUDDY_DEL_RES:
{
this.notificationSubject.next(decodeDelNotification(message));
}
break;
default:
break;
}
})
)
.subscribe();
}
public add(req: AddRequest): Observable<AddResponse> { public add(req: AddRequest): Observable<AddResponse> {
return this.protocolService return this.protocolService

View File

@ -5,6 +5,8 @@ export enum FileType {
Video = 'V', Video = 'V',
// F : 파일 // F : 파일
File = 'F', File = 'F',
// S : 사운드파일
Sound = 'S',
// "" 빈값이면 모든 타입을 내려줌 // "" 빈값이면 모든 타입을 내려줌
All = '' All = ''
} }

View File

@ -7,18 +7,26 @@ import {
PacketBodyValue, PacketBodyValue,
ProtocolDecoder, ProtocolDecoder,
ProtocolMessage, ProtocolMessage,
decodeProtocolMessage decodeProtocolMessage,
ProtocolNotification
} from '@ucap-webmessenger/protocol'; } from '@ucap-webmessenger/protocol';
export interface AddRequest extends ProtocolRequest { export interface AddRequest extends ProtocolRequest {
// 0. 동료그룹이름 /** 0. 동료그룹이름 */
groupName: string; groupName: string;
} }
export interface AddResponse extends ProtocolResponse { export interface AddResponse extends ProtocolResponse {
// 0: 동료그룹SEQ(n) /** 0: 동료그룹SEQ(n) */
groupSeq: number; groupSeq: number;
// 1: 동료그룹이름(s) /** 1: 동료그룹이름(s) */
groupName: string;
}
export interface AddNotification extends ProtocolNotification {
/** 0: 동료그룹SEQ(n) */
groupSeq: number;
/** 1: 동료그룹이름(s) */
groupName: string; groupName: string;
} }
@ -38,3 +46,12 @@ export const decodeAdd: ProtocolDecoder<AddResponse> = (
groupName: message.bodyList[1] groupName: message.bodyList[1]
} as AddResponse); } as AddResponse);
}; };
export const decodeAddNotification: ProtocolDecoder<AddNotification> = (
message: ProtocolMessage
) => {
return decodeProtocolMessage(message, {
groupSeq: message.bodyList[0],
groupName: message.bodyList[1]
} as AddNotification);
};

View File

@ -7,16 +7,22 @@ import {
PacketBodyValue, PacketBodyValue,
ProtocolDecoder, ProtocolDecoder,
ProtocolMessage, ProtocolMessage,
decodeProtocolMessage decodeProtocolMessage,
ProtocolNotification
} from '@ucap-webmessenger/protocol'; } from '@ucap-webmessenger/protocol';
export interface DelRequest extends ProtocolRequest { export interface DelRequest extends ProtocolRequest {
// 0: 동료그룹SEQ(n) /** 동료그룹SEQ(n) */
groupSeq: number; groupSeq: number;
} }
export interface DelResponse extends ProtocolResponse { export interface DelResponse extends ProtocolResponse {
// 0: 동료그룹SEQ(n) /** 동료그룹SEQ(n) */
groupSeq: number;
}
export interface DelNotification extends ProtocolNotification {
/** 동료그룹SEQ(n) */
groupSeq: number; groupSeq: number;
} }
@ -35,3 +41,11 @@ export const decodeDel: ProtocolDecoder<DelResponse> = (
groupSeq: message.bodyList[0] groupSeq: message.bodyList[0]
} as DelResponse); } as DelResponse);
}; };
export const decodeDelNotification: ProtocolDecoder<DelNotification> = (
message: ProtocolMessage
) => {
return decodeProtocolMessage(message, {
groupSeq: message.bodyList[0]
} as DelNotification);
};

View File

@ -7,24 +7,34 @@ import {
PacketBodyValue, PacketBodyValue,
ProtocolDecoder, ProtocolDecoder,
ProtocolMessage, ProtocolMessage,
decodeProtocolMessage decodeProtocolMessage,
ProtocolNotification
} from '@ucap-webmessenger/protocol'; } from '@ucap-webmessenger/protocol';
export interface UpdateRequest extends ProtocolRequest { export interface UpdateRequest extends ProtocolRequest {
// 0: 동료그룹SEQ(n) /** 0: 동료그룹SEQ(n) */
groupSeq: number; groupSeq: number;
// 1: 동료그룹이름(s) /** 1: 동료그룹이름(s) */
groupName: string; groupName: string;
// 2n: 사용자SEQ(n)... /** 2n: 사용자SEQ(n)... */
userSeqs: number[]; userSeqs: number[];
} }
export interface UpdateResponse extends ProtocolResponse { export interface UpdateResponse extends ProtocolResponse {
// 0: 동료그룹SEQ(n) /** 0: 동료그룹SEQ(n) */
groupSeq: number; groupSeq: number;
// 1: 동료그룹이름(s) /** 1: 동료그룹이름(s) */
groupName: string; groupName: string;
// 2n: 사용자SEQ(n)... /** 2n: 사용자SEQ(n)... */
userSeqs: number[];
}
export interface UpdateNotification extends ProtocolNotification {
/** 0: 동료그룹SEQ(n) */
groupSeq: number;
/** 1: 동료그룹이름(s) */
groupName: string;
/** 2n: 사용자SEQ(n)... */
userSeqs: number[]; userSeqs: number[];
} }
@ -90,3 +100,18 @@ export const decodeUpdate2: ProtocolDecoder<UpdateResponse> = (
userSeqs: userSeqArray userSeqs: userSeqArray
} as UpdateResponse); } as UpdateResponse);
}; };
export const decodeUpdate2Notification: ProtocolDecoder<UpdateNotification> = (
message: ProtocolMessage
) => {
let userSeqArray: number[] = [];
if (message.bodyList.length > 2) {
userSeqArray = message.bodyList.slice(2);
}
return decodeProtocolMessage(message, {
groupSeq: message.bodyList[0],
groupName: message.bodyList[1],
userSeqs: userSeqArray
} as UpdateNotification);
};

View File

@ -1,7 +1,12 @@
import {
SSVC_TYPE_GROUP_UPD_RES2,
SSVC_TYPE_GROUP_ADD_RES,
SSVC_TYPE_GROUP_DEL_RES
} from './../types/service';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { map, take } from 'rxjs/operators'; import { map, take, share, filter, tap } from 'rxjs/operators';
import { ProtocolService } from '@ucap-webmessenger/protocol'; import { ProtocolService } from '@ucap-webmessenger/protocol';
import { import {
@ -15,13 +20,17 @@ import {
AddRequest, AddRequest,
encodeAdd, encodeAdd,
decodeAdd, decodeAdd,
AddResponse AddResponse,
decodeAddNotification,
AddNotification
} from '../protocols/add'; } from '../protocols/add';
import { import {
DelRequest, DelRequest,
encodeDel, encodeDel,
decodeDel, decodeDel,
DelResponse DelResponse,
decodeDelNotification,
DelNotification
} from '../protocols/del'; } from '../protocols/del';
import { import {
UpdateRequest, UpdateRequest,
@ -29,13 +38,54 @@ import {
decodeUpdate, decodeUpdate,
encodeUpdate2, encodeUpdate2,
decodeUpdate2, decodeUpdate2,
UpdateResponse UpdateResponse,
UpdateNotification,
decodeUpdate2Notification
} from '../protocols/update'; } from '../protocols/update';
type Notifications = UpdateNotification | AddNotification | DelNotification;
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class GroupProtocolService { export class GroupProtocolService {
constructor(private protocolService: ProtocolService) {} private notificationSubject: Subject<Notifications>;
public notification$: Observable<Notifications>;
constructor(private protocolService: ProtocolService) {
this.notificationSubject = new Subject();
this.notification$ = this.notificationSubject.asObservable().pipe(share());
this.protocolService.serverMessage
.pipe(
filter(message => message.serviceType === SVC_TYPE_GROUP),
tap(message => {
switch (message.subServiceType) {
case SSVC_TYPE_GROUP_UPD_RES2:
{
this.notificationSubject.next(
decodeUpdate2Notification(message)
);
}
break;
case SSVC_TYPE_GROUP_ADD_RES:
{
this.notificationSubject.next(decodeAddNotification(message));
}
break;
case SSVC_TYPE_GROUP_DEL_RES:
{
this.notificationSubject.next(decodeDelNotification(message));
}
break;
default:
break;
}
})
)
.subscribe();
}
public add(req: AddRequest): Observable<AddResponse> { public add(req: AddRequest): Observable<AddResponse> {
return this.protocolService return this.protocolService

View File

@ -103,11 +103,14 @@ import {
encodeUpdateFont, encodeUpdateFont,
decodeUpdateFont, decodeUpdateFont,
decodeUpdateFontNotification, decodeUpdateFontNotification,
UpdateFontNotification UpdateFontNotification,
decodeUpdateNotification,
UpdateNotification
} from '../protocols/update'; } from '../protocols/update';
type Notifications = type Notifications =
| UpdateFontNotification | UpdateFontNotification
| UpdateNotification
| InviteNotification | InviteNotification
| ExitNotification | ExitNotification
| ExitForcingNotification; | ExitForcingNotification;
@ -130,7 +133,9 @@ export class RoomProtocolService {
switch (message.subServiceType) { switch (message.subServiceType) {
case SSVC_TYPE_ROOM_UPD_RES: case SSVC_TYPE_ROOM_UPD_RES:
{ {
this.notificationSubject.next(decodeUpdate(message)); this.notificationSubject.next(
decodeUpdateNotification(message)
);
} }
break; break;
case SSVC_TYPE_ROOM_INVITE_NOTI: case SSVC_TYPE_ROOM_INVITE_NOTI:

View File

@ -1,20 +1,23 @@
<div class="bubble-main"> <div class="bubble-main">
<!--파일명에 따라 doc exe hwp ppt xls zip 으로 추가되고 나머지 파일 명은 file로 기간이 만료된 파일은 그뒤에 disable도 추가--> <!--파일명에 따라 doc exe hwp ppt xls zip 으로 추가되고 나머지 파일 명은 file로 기간이 만료된 파일은 그뒤에 disable도 추가-->
<div class="file-img ppt"></div> <div class="file-img" [ngClass]="fileInfo.FileExt"></div>
<ul class="file-info"> <ul class="file-info">
<li class="file-name"> <li class="file-name">
{{ fileInfo.FileName }} {{ fileInfo.FileName }}
</li> </li>
<li class="file-size"> <li class="file-size">
{{ fileInfo.AttSize }} {{ fileInfo.AttSize | ucapBytes }}
</li> </li>
<li class="file-ext"> <li class="file-ext">
{{ fileInfo.FileExt }} {{ fileInfo.FileExt }}
</li> </li>
</ul> </ul>
</div> </div>
<div *ngIf="fileInfo && fileInfo.AttSEQ" class="btn-box"> <div class="btn-box">
<ul> <ul *ngIf="expired">
<li>기간이 만료된 파일입니다.</li>
</ul>
<ul *ngIf="!expired && fileInfo && fileInfo.AttSEQ">
<li> <li>
<button mat-button (click)="onClickSave()">Save</button> <button mat-button (click)="onClickSave()">Save</button>
</li> </li>

View File

@ -33,13 +33,15 @@
background-image: url(/assets/images/file/icon_talk_hwp_d.png); background-image: url(/assets/images/file/icon_talk_hwp_d.png);
} }
} }
&.ppt { &.ppt,
&.pptx {
background-image: url(/assets/images/file/icon_talk_ppt.png); background-image: url(/assets/images/file/icon_talk_ppt.png);
&.disable { &.disable {
background-image: url(/assets/images/file/icon_talk_ppt_d.png); background-image: url(/assets/images/file/icon_talk_ppt_d.png);
} }
} }
&.xls { &.xls,
&.xlsx {
background-image: url(/assets/images/file/icon_talk_xls.png); background-image: url(/assets/images/file/icon_talk_xls.png);
&.disable { &.disable {
background-image: url(/assets/images/file/icon_talk_xls_d.png); background-image: url(/assets/images/file/icon_talk_xls_d.png);

View File

@ -10,6 +10,9 @@ import { NGXLogger } from 'ngx-logger';
export class AttachFileComponent implements OnInit { export class AttachFileComponent implements OnInit {
@Input() @Input()
fileInfo: FileInfo; fileInfo: FileInfo;
@Input()
expired = false;
@Output() @Output()
save = new EventEmitter<string>(); save = new EventEmitter<string>();

View File

@ -1,9 +1,35 @@
<ng-container *ngIf="fileInfo && fileInfo.FileType" [ngSwitch]="fileInfo.FileType"> <ng-container
<ucap-chat-message-box-attach-file *ngSwitchCase="FileType.File" [fileInfo]="fileInfo" (save)="onSave($event)"> *ngIf="fileInfo && fileInfo.FileType"
[ngSwitch]="fileInfo.FileType"
>
<ucap-chat-message-box-attach-file
*ngSwitchCase="FileType.File"
[fileInfo]="fileInfo"
[expired]="getExpiredFile()"
(save)="onSave($event)"
>
</ucap-chat-message-box-attach-file> </ucap-chat-message-box-attach-file>
<ucap-chat-message-box-image *ngSwitchCase="FileType.Image" [fileInfo]="fileInfo" <ucap-chat-message-box-attach-file
(click)="onClickImageViewer(fileInfo)"></ucap-chat-message-box-image> *ngSwitchCase="FileType.Sound"
<ucap-chat-message-box-video *ngSwitchCase="FileType.Video" [fileInfo]="fileInfo" [fileInfo]="fileInfo"
(click)="onClickImageViewer(fileInfo)"></ucap-chat-message-box-video> [expired]="getExpiredFile()"
<ucap-chat-message-box-text *ngSwitchDefault [message]="message"></ucap-chat-message-box-text> (save)="onSave($event)"
>
</ucap-chat-message-box-attach-file>
<ucap-chat-message-box-image
*ngSwitchCase="FileType.Image"
[fileInfo]="fileInfo"
[expired]="getExpiredFile()"
(click)="onClickImageViewer(fileInfo)"
></ucap-chat-message-box-image>
<ucap-chat-message-box-video
*ngSwitchCase="FileType.Video"
[fileInfo]="fileInfo"
[expired]="getExpiredFile()"
(click)="onClickImageViewer(fileInfo)"
></ucap-chat-message-box-video>
<ucap-chat-message-box-text
*ngSwitchDefault
[message]="message"
></ucap-chat-message-box-text>
</ng-container> </ng-container>

View File

@ -1,5 +1,5 @@
import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core'; import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
import { Info } from '@ucap-webmessenger/protocol-event'; import { Info, InfoResponse } from '@ucap-webmessenger/protocol-event';
import { StatusCode } from '@ucap-webmessenger/api'; import { StatusCode } from '@ucap-webmessenger/api';
import { FileType } from '@ucap-webmessenger/protocol-file'; import { FileType } from '@ucap-webmessenger/protocol-file';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
@ -13,6 +13,8 @@ import { FileInfo } from '../../models/file-info.json';
export class FileComponent implements OnInit { export class FileComponent implements OnInit {
@Input() @Input()
message: Info; message: Info;
@Input()
eventInfoStatus: InfoResponse;
@Output() @Output()
save = new EventEmitter<{ fileInfo: FileInfo; type: string }>(); save = new EventEmitter<{ fileInfo: FileInfo; type: string }>();
@ -34,6 +36,17 @@ export class FileComponent implements OnInit {
} }
} }
getExpiredFile() {
if (
!!this.eventInfoStatus &&
this.eventInfoStatus.validFileBaseSeq < this.message.seq
) {
return false;
} else {
return true;
}
}
onClickImageViewer(fileInfo: FileInfo) { onClickImageViewer(fileInfo: FileInfo) {
this.imageViewer.emit(this.fileInfo); this.imageViewer.emit(this.fileInfo);
} }

View File

@ -10,6 +10,8 @@ import { FileInfo } from '../../models/file-info.json';
export class ImageComponent implements OnInit { export class ImageComponent implements OnInit {
@Input() @Input()
fileInfo: FileInfo; fileInfo: FileInfo;
@Input()
expired = false;
constructor(private logger: NGXLogger) {} constructor(private logger: NGXLogger) {}

View File

@ -10,6 +10,8 @@ import { FileInfo } from '../../models/file-info.json';
export class VideoComponent implements OnInit { export class VideoComponent implements OnInit {
@Input() @Input()
fileInfo: FileInfo; fileInfo: FileInfo;
@Input()
expired = false;
constructor(private logger: NGXLogger) {} constructor(private logger: NGXLogger) {}

View File

@ -103,6 +103,7 @@
</ucap-chat-message-box-mass> </ucap-chat-message-box-mass>
<ucap-chat-message-box-file <ucap-chat-message-box-file
*ngSwitchCase="EventType.File" *ngSwitchCase="EventType.File"
[eventInfoStatus]="eventInfoStatus"
[message]="message" [message]="message"
(save)="onSave($event)" (save)="onSave($event)"
(imageViewer)="onImageViewer($event)" (imageViewer)="onImageViewer($event)"

View File

@ -7,7 +7,11 @@ import {
ViewEncapsulation ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { Info, EventType } from '@ucap-webmessenger/protocol-event'; import {
Info,
EventType,
InfoResponse
} from '@ucap-webmessenger/protocol-event';
import { import {
LoginResponse, LoginResponse,
UserInfo UserInfo
@ -28,6 +32,8 @@ export class MessagesComponent implements OnInit {
@Input() @Input()
messages: Info[]; messages: Info[];
@Input() @Input()
eventInfoStatus?: InfoResponse;
@Input()
userInfos?: UserInfo[]; userInfos?: UserInfo[];
@Input() @Input()
sessionVerInfo: VersionInfo2Response; sessionVerInfo: VersionInfo2Response;