This commit is contained in:
병준 박 2019-10-25 09:46:32 +09:00
commit 940c28218d
14 changed files with 318 additions and 47 deletions

View File

@ -15,9 +15,7 @@
"e2e": "ng e2e" "e2e": "ng e2e"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {},
"tslib": "^1.10.0"
},
"devDependencies": { "devDependencies": {
"@angular-builders/custom-webpack": "^8.2.0", "@angular-builders/custom-webpack": "^8.2.0",
"@angular-devkit/build-angular": "~0.803.14", "@angular-devkit/build-angular": "~0.803.14",

View File

@ -42,8 +42,8 @@
</button> </button>
<mat-menu #contactMenu="matMenu"> <mat-menu #contactMenu="matMenu">
<button mat-menu-item (click)="selectContact()"> <button mat-menu-item (click)="onClickContextMenu('CLOSE_ROOM')">
Contact Info 방닫기
</button> </button>
</mat-menu> </mat-menu>
</div> </div>

View File

@ -1,3 +1,4 @@
import { forward } from './../../../store/messenger/event/actions';
import { import {
Component, Component,
OnInit, OnInit,
@ -32,7 +33,11 @@ import * as ChatStore from '@app/store/messenger/chat';
import * as RoomStore from '@app/store/messenger/room'; import * as RoomStore from '@app/store/messenger/room';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types'; import {
EnvironmentsInfo,
KEY_ENVIRONMENTS_INFO,
UserSelectDialogType
} from '@app/types';
import { RoomInfo, UserInfo, RoomType } from '@ucap-webmessenger/protocol-room'; import { RoomInfo, UserInfo, RoomType } from '@ucap-webmessenger/protocol-room';
import { tap, take } from 'rxjs/operators'; import { tap, take } from 'rxjs/operators';
import { FileInfo } from '@ucap-webmessenger/ui-chat'; import { FileInfo } from '@ucap-webmessenger/ui-chat';
@ -40,22 +45,11 @@ import { KEY_VER_INFO } from '@app/types/ver-info.type';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
import { MatMenuTrigger } from '@angular/material'; import { MatMenuTrigger } from '@angular/material';
import { CommonApiService } from '@ucap-webmessenger/api-common'; import { CommonApiService } from '@ucap-webmessenger/api-common';
import { import {
DeleteMessageDialogComponent, CreateChatDialogComponent,
DeleteMessageDialogData, CreateChatDialogData,
DeleteMessageDialogResult CreateChatDialogResult
} from '@app/layouts/messenger/dialogs/message/delete-message.dialog.component'; } from '../dialogs/chat/create-chat.dialog.component';
import {
RelayMessageDialogComponent,
RelayMessageDialogData,
RelayMessageDialogResult
} from '@app/layouts/messenger/dialogs/message/relay-message.dialog.component';
import {
RecallMessageDialogComponent,
RecallMessageDialogData,
RecallMessageDialogResult
} from '@app/layouts/messenger/dialogs/message/recall-message.dialog.component';
@Component({ @Component({
selector: 'app-layout-messenger-messages', selector: 'app-layout-messenger-messages',
@ -320,16 +314,48 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
case 'REPLAY': case 'REPLAY':
{ {
const result = await this.dialogService.open< const result = await this.dialogService.open<
RelayMessageDialogComponent, CreateChatDialogComponent,
RelayMessageDialogData, CreateChatDialogData,
RelayMessageDialogResult CreateChatDialogResult
>(RelayMessageDialogComponent, { >(CreateChatDialogComponent, {
width: '220px', width: '600px',
height: '500px',
data: { data: {
title: 'Logout', type: UserSelectDialogType.MessageForward,
message: 'Logout ?' title: 'MessageForward',
ignoreRoom: [this.roomInfo]
} }
}); });
if (!!result && !!result.choice && result.choice) {
const userSeqs: number[] = [];
let roomSeq = '';
if (
!!result.selectedUserList &&
result.selectedUserList.length > 0
) {
result.selectedUserList.map(user => userSeqs.push(user.seq));
}
if (!!result.selectedRoom) {
roomSeq = result.selectedRoom.roomSeq;
}
if (userSeqs.length > 0 || roomSeq.trim().length > 0) {
this.store.dispatch(
EventStore.forward({
senderSeq: this.loginRes.userSeq,
req: {
roomSeq: '-999',
eventType: message.type,
sentMessage: message.sentMessage
},
trgtUserSeqs: userSeqs,
trgtRoomSeq: roomSeq
})
);
}
}
} }
break; break;
case 'REPLAY_TO_ME': case 'REPLAY_TO_ME':
@ -389,4 +415,16 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
break; break;
} }
} }
onClickContextMenu(menuType: string) {
switch (menuType) {
case 'CLOSE_ROOM':
{
this.store.dispatch(ChatStore.clearSelectedRoom());
}
break;
default:
break;
}
}
} }

View File

@ -25,7 +25,10 @@
fxLayoutGap.xs="0" fxLayoutGap.xs="0"
> >
<div fxFlex class="container"> <div fxFlex class="container">
<mat-tab-group mat-stretch-tabs> <mat-tab-group
mat-stretch-tabs
(selectedTabChange)="onSelectedTabChange($event)"
>
<mat-tab> <mat-tab>
<ng-template mat-tab-label> <ng-template mat-tab-label>
<mat-icon>group</mat-icon> <mat-icon>group</mat-icon>
@ -98,12 +101,16 @@
[roomInfo]="room" [roomInfo]="room"
[roomUserInfo]="getRoomUserList(room)" [roomUserInfo]="getRoomUserList(room)"
[sessionVerinfo]="sessionVerinfo" [sessionVerinfo]="sessionVerinfo"
[checkable]="getCheckableRoom(room)"
[isChecked]="getCheckedRoom(room)"
[multiCheckable]="false"
(checkRoom)="onCheckRoom($event)"
> >
</ucap-room-list-item> </ucap-room-list-item>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
</div> </div>
<div fxFlex="150px"> <div fxFlex="150px" *ngIf="isShowSelectedUserList">
<mat-chip-list aria-label="User selection"> <mat-chip-list aria-label="User selection">
<mat-chip <mat-chip
*ngFor="let userInfo of selectedUserList" *ngFor="let userInfo of selectedUserList"

View File

@ -1,7 +1,17 @@
import { UserSelectDialogType } from './../../../../types/userselect.dialog.type'; import { UserSelectDialogType } from './../../../../types/userselect.dialog.type';
import { Component, OnInit, OnDestroy, Inject } from '@angular/core'; import {
Component,
OnInit,
OnDestroy,
Inject,
EventEmitter
} from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; import {
MatDialogRef,
MAT_DIALOG_DATA,
MatTabChangeEvent
} from '@angular/material';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
import { Observable, combineLatest, Subscription, of } from 'rxjs'; import { Observable, combineLatest, Subscription, of } from 'rxjs';
import { map, tap, catchError } from 'rxjs/operators'; import { map, tap, catchError } from 'rxjs/operators';
@ -44,11 +54,14 @@ export interface CreateChatDialogData {
title: string; title: string;
/** CASE :: EditMember */ /** CASE :: EditMember */
group?: GroupDetailData; group?: GroupDetailData;
/** CASE :: EventForward */
ignoreRoom?: RoomInfo[];
} }
export interface CreateChatDialogResult { export interface CreateChatDialogResult {
choice: boolean; choice: boolean;
selectedUserList?: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[]; selectedUserList?: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[];
selectedRoom?: RoomInfo;
groupName?: string; groupName?: string;
oldGroup?: GroupDetailData; oldGroup?: GroupDetailData;
} }
@ -101,6 +114,8 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy {
// 수집 데이터 // 수집 데이터
selectedUserList: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[] = []; selectedUserList: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[] = [];
isShowSelectedUserList = true;
selectedRoom: RoomInfo;
inputForm: FormGroup; inputForm: FormGroup;
@ -220,6 +235,16 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy {
} }
} }
onSelectedTabChange(tabChangeEvent: MatTabChangeEvent): void {
if (tabChangeEvent.index === 2) {
this.selectedUserList = [];
this.isShowSelectedUserList = false;
} else {
this.selectedRoom = null;
this.isShowSelectedUserList = true;
}
}
/** 유저검색 */ /** 유저검색 */
onKeyDownEnterOrganizationTenantSearch(params: { onKeyDownEnterOrganizationTenantSearch(params: {
companyCode: string; companyCode: string;
@ -323,6 +348,15 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy {
} }
} }
/** 대화방 > 대화방 선택 :: 해당 팝업에서는 대화방을 중복 선택하지 않는다 */
onCheckRoom(params: { isChecked: boolean; roomInfo: RoomInfo }) {
if (params.isChecked) {
this.selectedRoom = params.roomInfo;
} else {
this.selectedRoom = null;
}
}
/** 그룹>부서원 리스트의 ischecked 를 판단. */ /** 그룹>부서원 리스트의 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) {
@ -334,6 +368,22 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy {
return false; return false;
} }
getCheckableRoom(roomInfo: RoomInfo) {
if (!!this.data.ignoreRoom && this.data.ignoreRoom.length > 0) {
return !(
this.data.ignoreRoom.filter(room => room.roomSeq === roomInfo.roomSeq)
.length > 0
);
}
return true;
}
getCheckedRoom(roomInfo: RoomInfo) {
if (!!this.selectedRoom) {
return this.selectedRoom.roomSeq === roomInfo.roomSeq;
}
return false;
}
/** 선택된 사용자 취소 */ /** 선택된 사용자 취소 */
onClickDeleteUser(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) { onClickDeleteUser(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) {
this.selectedUserList = this.selectedUserList.filter( this.selectedUserList = this.selectedUserList.filter(
@ -346,6 +396,7 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy {
this.dialogRef.close({ this.dialogRef.close({
choice, choice,
selectedUserList: this.selectedUserList, selectedUserList: this.selectedUserList,
selectedRoom: this.selectedRoom,
groupName: groupName:
this.data.type === UserSelectDialogType.NewGroup this.data.type === UserSelectDialogType.NewGroup
? this.inputForm.get('groupName').value ? this.inputForm.get('groupName').value

View File

@ -10,6 +10,10 @@ export const selectedRoom = createAction(
props<{ roomSeq: string }>() props<{ roomSeq: string }>()
); );
export const clearSelectedRoom = createAction(
'[Messenger::Chat] clearSelectedRoom'
);
export const newEventMessage = createAction( export const newEventMessage = createAction(
'[Messenger::Chat] newEventMessage', '[Messenger::Chat] newEventMessage',
props<{ props<{

View File

@ -5,7 +5,8 @@ import {
selectedMassDetail, selectedMassDetail,
massTalkDownloadFailure, massTalkDownloadFailure,
massTalkDownload, massTalkDownload,
massTalkDownloadSuccess massTalkDownloadSuccess,
clearSelectedRoom
} from './actions'; } from './actions';
export const reducer = createReducer( export const reducer = createReducer(
@ -17,6 +18,13 @@ export const reducer = createReducer(
}; };
}), }),
on(clearSelectedRoom, (state, action) => {
return {
...state,
selectedRoom: null
};
}),
on(selectedMassDetail, (state, action) => { on(selectedMassDetail, (state, action) => {
return { return {
...state, ...state,

View File

@ -72,6 +72,29 @@ export const sendNotification = createAction(
props<{ noti: SendNotification }>() props<{ noti: SendNotification }>()
); );
export const forward = createAction(
'[Messenger::Event] forward',
props<{
senderSeq: number;
req: SendRequest;
trgtUserSeqs?: number[];
trgtRoomSeq?: string;
}>()
);
export const forwardFailure = createAction(
'[Messenger::Event] Forward Failure',
props<{ error: any }>()
);
export const forwardAfterRoomOpen = createAction(
'[Messenger::Event] forwardAfterRoomOpen',
props<{
senderSeq: number;
req: SendRequest;
trgtUserSeqs?: number[];
trgtRoomSeq?: string;
}>()
);
export const read = createAction( export const read = createAction(
'[Messenger::Event] read', '[Messenger::Event] read',
props<ReadRequest>() props<ReadRequest>()

View File

@ -29,6 +29,7 @@ import {
} from '@ucap-webmessenger/protocol-event'; } from '@ucap-webmessenger/protocol-event';
import * as ChatStore from '@app/store/messenger/chat'; import * as ChatStore from '@app/store/messenger/chat';
import * as EventStore from '@app/store/messenger/event';
import * as SyncStore from '@app/store/messenger/sync'; import * as SyncStore from '@app/store/messenger/sync';
import { import {
@ -51,12 +52,20 @@ import {
delFailure, delFailure,
delInfoList, delInfoList,
cancel, cancel,
cancelFailure cancelFailure,
forward,
forwardFailure,
forwardAfterRoomOpen
} from './actions'; } from './actions';
import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { RoomInfo } from '@ucap-webmessenger/protocol-room'; import {
RoomInfo,
RoomProtocolService,
OpenResponse
} from '@ucap-webmessenger/protocol-room';
import { LoginInfo, KEY_LOGIN_INFO } from '@app/types'; import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';
import { Dictionary } from '@ngrx/entity'; import { Dictionary } from '@ngrx/entity';
import { openSuccess, openFailure } from '../room';
@Injectable() @Injectable()
export class Effects { export class Effects {
@ -220,6 +229,67 @@ export class Effects {
{ dispatch: false } { dispatch: false }
); );
forward$ = createEffect(
() => {
return this.actions$.pipe(
ofType(forward),
map(action => {
if (!!action.trgtRoomSeq) {
this.store.dispatch(
ChatStore.selectedRoom({ roomSeq: action.trgtRoomSeq })
);
this.store.dispatch(
EventStore.send({
senderSeq: action.senderSeq,
req: {
roomSeq: action.trgtRoomSeq,
eventType: action.req.eventType,
sentMessage: action.req.sentMessage
}
})
);
} else if (!!action.trgtUserSeqs && action.trgtUserSeqs.length > 0) {
// 방오픈 후 대화전달.
this.store.dispatch(forwardAfterRoomOpen(action));
}
})
);
},
{ dispatch: false }
);
forwardAfterRoomOpen$ = createEffect(() =>
this.actions$.pipe(
ofType(forwardAfterRoomOpen),
exhaustMap(action => {
return this.roomProtocolService
.open({
divCd: 'forwardOpen',
userSeqs: action.trgtUserSeqs
})
.pipe(
map((res: OpenResponse) => {
return openSuccess({ res });
}),
map(res => {
this.store.dispatch(
EventStore.send({
senderSeq: action.senderSeq,
req: {
roomSeq: res.res.roomSeq,
eventType: action.req.eventType,
sentMessage: action.req.sentMessage
}
})
);
return res;
}),
catchError(error => of(openFailure({ error })))
);
})
)
);
newInfo$ = createEffect( newInfo$ = createEffect(
() => { () => {
return this.actions$.pipe( return this.actions$.pipe(
@ -360,6 +430,7 @@ export class Effects {
private actions$: Actions, private actions$: Actions,
private store: Store<any>, private store: Store<any>,
private eventProtocolService: EventProtocolService, private eventProtocolService: EventProtocolService,
private roomProtocolService: RoomProtocolService,
private sessionStorageService: SessionStorageService, private sessionStorageService: SessionStorageService,
private logger: NGXLogger private logger: NGXLogger
) {} ) {}

View File

@ -181,9 +181,17 @@ export class Effects {
exit$ = createEffect(() => exit$ = createEffect(() =>
this.actions$.pipe( this.actions$.pipe(
ofType(exit), ofType(exit),
exhaustMap(req => { withLatestFrom(
this.store.pipe(
select((state: any) => state.messenger.room.roomInfo as RoomInfo)
)
),
exhaustMap(([req, roomInfo]) => {
return this.roomProtocolService.exit(req).pipe( return this.roomProtocolService.exit(req).pipe(
map((res: ExitResponse) => { map((res: ExitResponse) => {
if (!!roomInfo && roomInfo.roomSeq === res.roomSeq) {
this.store.dispatch(ChatStore.clearSelectedRoom());
}
return exitSuccess({ res }); return exitSuccess({ res });
}), }),
catchError(error => of(exitFailure({ error }))) catchError(error => of(exitFailure({ error })))

View File

@ -28,6 +28,8 @@ import {
import * as RoomStore from '@app/store/messenger/room'; import * as RoomStore from '@app/store/messenger/room';
import { RoomInfo } from '@ucap-webmessenger/protocol-room'; import { RoomInfo } from '@ucap-webmessenger/protocol-room';
import { EventType } from '@ucap-webmessenger/protocol-event';
import { FileType } from '@ucap-webmessenger/protocol-file';
export const reducer = createReducer( export const reducer = createReducer(
initialState, initialState,
@ -99,10 +101,29 @@ export const reducer = createReducer(
}), }),
on(updateRoomForNewEventMessage, (state, action) => { on(updateRoomForNewEventMessage, (state, action) => {
let finalEventMessage: string = action.info.sentMessage;
switch (action.info.type) {
case EventType.Sticker:
finalEventMessage = '스티커';
break;
case EventType.File:
{
const contentJson = JSON.parse(finalEventMessage);
if (contentJson.FileType === FileType.Image) {
finalEventMessage = '이미지';
} else {
finalEventMessage = '첨부파일';
}
}
break;
case EventType.VideoConference:
finalEventMessage = '화상회의';
break;
}
const roomInfo = { const roomInfo = {
...state.room.entities[action.roomSeq], ...state.room.entities[action.roomSeq],
finalEventDate: action.info.sendDate, finalEventDate: action.info.sendDate,
finalEventMessage: action.info.sentMessage finalEventMessage
}; };
return { return {

View File

@ -16,7 +16,7 @@
<div class="num" *ngIf="roomInfo.roomType === RoomType.Multi"> <div class="num" *ngIf="roomInfo.roomType === RoomType.Multi">
{{ roomInfo.joinUserCount }}명 {{ roomInfo.joinUserCount }}명
</div> </div>
<div *ngIf="!roomInfo.receiveAlarm"> <div *ngIf="!checkable && !roomInfo.receiveAlarm">
<mat-icon>notifications_off</mat-icon> <mat-icon>notifications_off</mat-icon>
</div> </div>
</div> </div>
@ -25,6 +25,15 @@
<div class="date">{{ roomInfo.finalEventDate }}</div> <div class="date">{{ roomInfo.finalEventDate }}</div>
</dd> </dd>
<dd *ngIf="checkable">
<mat-checkbox
#checkbox
[checked]="isChecked"
(change)="onChangeCheck(checkbox.checked, roomInfo)"
(click)="$event.stopPropagation()"
>
</mat-checkbox>
</dd>
</dl> </dl>
<!-- <span class="noti">1</span> --> <!-- <span class="noti">1</span> -->
<span <span

View File

@ -1,4 +1,11 @@
import { Component, OnInit, Input } from '@angular/core'; import {
Component,
OnInit,
Input,
Output,
EventEmitter,
ViewChildren
} from '@angular/core';
import { import {
RoomInfo, RoomInfo,
UserInfoShort, UserInfoShort,
@ -7,13 +14,9 @@ import {
} from '@ucap-webmessenger/protocol-room'; } from '@ucap-webmessenger/protocol-room';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
import { EventType } from '@ucap-webmessenger/protocol-event'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { FileInfo, StickerInfo } from '@ucap-webmessenger/ui-chat'; import { FormsModule } from '@angular/forms';
import { FileType } from '@ucap-webmessenger/protocol-file'; import { MatCheckbox } from '@angular/material';
import {
LoginResponse,
UserInfo
} from '@ucap-webmessenger/protocol-authentication';
@Component({ @Component({
selector: 'ucap-room-list-item', selector: 'ucap-room-list-item',
@ -29,6 +32,18 @@ export class ListItemComponent implements OnInit {
roomUserInfo: (RoomUserInfo | UserInfoShort)[]; roomUserInfo: (RoomUserInfo | UserInfoShort)[];
@Input() @Input()
sessionVerinfo: VersionInfo2Response; sessionVerinfo: VersionInfo2Response;
@Input()
checkable = false;
@Input()
isChecked = false;
@Input()
multiCheckable = true;
@Output()
checkRoom = new EventEmitter<{
isChecked: boolean;
roomInfo: RoomInfo;
}>();
imagePath: string; imagePath: string;
defaultPath = 'assets/images/img_nophoto_50.png'; defaultPath = 'assets/images/img_nophoto_50.png';
@ -95,4 +110,21 @@ export class ListItemComponent implements OnInit {
return roomName; return roomName;
} }
} }
// getChecked(value: boolean, roomInfo: RoomInfo) {
// if (value && !this.multiCheckable) {
// if (this.selected === roomInfo.roomSeq) {
// return true;
// } else {
// return false;
// }
// }
// }
onChangeCheck(value: boolean, roomInfo: RoomInfo) {
this.checkRoom.emit({
isChecked: value,
roomInfo
});
}
} }

View File

@ -8,7 +8,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { ListItemComponent } from './components/list-item.component'; import { ListItemComponent } from './components/list-item.component';
import { MatBadgeModule } from '@angular/material'; import { MatBadgeModule, MatCheckboxModule } from '@angular/material';
import { UCapUiModule } from '@ucap-webmessenger/ui'; import { UCapUiModule } from '@ucap-webmessenger/ui';
@ -26,6 +26,7 @@ const SERVICES = [];
MatIconModule, MatIconModule,
MatInputModule, MatInputModule,
MatBadgeModule, MatBadgeModule,
MatCheckboxModule,
UCapUiModule UCapUiModule
], ],