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"
},
"private": true,
"dependencies": {
"tslib": "^1.10.0"
},
"dependencies": {},
"devDependencies": {
"@angular-builders/custom-webpack": "^8.2.0",
"@angular-devkit/build-angular": "~0.803.14",

View File

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

View File

@ -1,3 +1,4 @@
import { forward } from './../../../store/messenger/event/actions';
import {
Component,
OnInit,
@ -32,7 +33,11 @@ import * as ChatStore from '@app/store/messenger/chat';
import * as RoomStore from '@app/store/messenger/room';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
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 { tap, take } from 'rxjs/operators';
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 { MatMenuTrigger } from '@angular/material';
import { CommonApiService } from '@ucap-webmessenger/api-common';
import {
DeleteMessageDialogComponent,
DeleteMessageDialogData,
DeleteMessageDialogResult
} from '@app/layouts/messenger/dialogs/message/delete-message.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';
CreateChatDialogComponent,
CreateChatDialogData,
CreateChatDialogResult
} from '../dialogs/chat/create-chat.dialog.component';
@Component({
selector: 'app-layout-messenger-messages',
@ -320,16 +314,48 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
case 'REPLAY':
{
const result = await this.dialogService.open<
RelayMessageDialogComponent,
RelayMessageDialogData,
RelayMessageDialogResult
>(RelayMessageDialogComponent, {
width: '220px',
CreateChatDialogComponent,
CreateChatDialogData,
CreateChatDialogResult
>(CreateChatDialogComponent, {
width: '600px',
height: '500px',
data: {
title: 'Logout',
message: 'Logout ?'
type: UserSelectDialogType.MessageForward,
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;
case 'REPLAY_TO_ME':
@ -389,4 +415,16 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
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"
>
<div fxFlex class="container">
<mat-tab-group mat-stretch-tabs>
<mat-tab-group
mat-stretch-tabs
(selectedTabChange)="onSelectedTabChange($event)"
>
<mat-tab>
<ng-template mat-tab-label>
<mat-icon>group</mat-icon>
@ -98,12 +101,16 @@
[roomInfo]="room"
[roomUserInfo]="getRoomUserList(room)"
[sessionVerinfo]="sessionVerinfo"
[checkable]="getCheckableRoom(room)"
[isChecked]="getCheckedRoom(room)"
[multiCheckable]="false"
(checkRoom)="onCheckRoom($event)"
>
</ucap-room-list-item>
</mat-tab>
</mat-tab-group>
</div>
<div fxFlex="150px">
<div fxFlex="150px" *ngIf="isShowSelectedUserList">
<mat-chip-list aria-label="User selection">
<mat-chip
*ngFor="let userInfo of selectedUserList"

View File

@ -1,7 +1,17 @@
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 { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import {
MatDialogRef,
MAT_DIALOG_DATA,
MatTabChangeEvent
} from '@angular/material';
import { NGXLogger } from 'ngx-logger';
import { Observable, combineLatest, Subscription, of } from 'rxjs';
import { map, tap, catchError } from 'rxjs/operators';
@ -44,11 +54,14 @@ export interface CreateChatDialogData {
title: string;
/** CASE :: EditMember */
group?: GroupDetailData;
/** CASE :: EventForward */
ignoreRoom?: RoomInfo[];
}
export interface CreateChatDialogResult {
choice: boolean;
selectedUserList?: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[];
selectedRoom?: RoomInfo;
groupName?: string;
oldGroup?: GroupDetailData;
}
@ -101,6 +114,8 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy {
// 수집 데이터
selectedUserList: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[] = [];
isShowSelectedUserList = true;
selectedRoom: RoomInfo;
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: {
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 를 판단. */
getCheckedUser(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) {
if (!!this.selectedUserList && this.selectedUserList.length > 0) {
@ -334,6 +368,22 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy {
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) {
this.selectedUserList = this.selectedUserList.filter(
@ -346,6 +396,7 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy {
this.dialogRef.close({
choice,
selectedUserList: this.selectedUserList,
selectedRoom: this.selectedRoom,
groupName:
this.data.type === UserSelectDialogType.NewGroup
? this.inputForm.get('groupName').value

View File

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

View File

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

View File

@ -72,6 +72,29 @@ export const sendNotification = createAction(
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(
'[Messenger::Event] read',
props<ReadRequest>()

View File

@ -29,6 +29,7 @@ import {
} from '@ucap-webmessenger/protocol-event';
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 {
@ -51,12 +52,20 @@ import {
delFailure,
delInfoList,
cancel,
cancelFailure
cancelFailure,
forward,
forwardFailure,
forwardAfterRoomOpen
} from './actions';
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 { Dictionary } from '@ngrx/entity';
import { openSuccess, openFailure } from '../room';
@Injectable()
export class Effects {
@ -220,6 +229,67 @@ export class Effects {
{ 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(
() => {
return this.actions$.pipe(
@ -360,6 +430,7 @@ export class Effects {
private actions$: Actions,
private store: Store<any>,
private eventProtocolService: EventProtocolService,
private roomProtocolService: RoomProtocolService,
private sessionStorageService: SessionStorageService,
private logger: NGXLogger
) {}

View File

@ -181,9 +181,17 @@ export class Effects {
exit$ = createEffect(() =>
this.actions$.pipe(
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(
map((res: ExitResponse) => {
if (!!roomInfo && roomInfo.roomSeq === res.roomSeq) {
this.store.dispatch(ChatStore.clearSelectedRoom());
}
return exitSuccess({ res });
}),
catchError(error => of(exitFailure({ error })))

View File

@ -28,6 +28,8 @@ import {
import * as RoomStore from '@app/store/messenger/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(
initialState,
@ -99,10 +101,29 @@ export const reducer = createReducer(
}),
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 = {
...state.room.entities[action.roomSeq],
finalEventDate: action.info.sendDate,
finalEventMessage: action.info.sentMessage
finalEventMessage
};
return {

View File

@ -16,7 +16,7 @@
<div class="num" *ngIf="roomInfo.roomType === RoomType.Multi">
{{ roomInfo.joinUserCount }}명
</div>
<div *ngIf="!roomInfo.receiveAlarm">
<div *ngIf="!checkable && !roomInfo.receiveAlarm">
<mat-icon>notifications_off</mat-icon>
</div>
</div>
@ -25,6 +25,15 @@
<div class="date">{{ roomInfo.finalEventDate }}</div>
</dd>
<dd *ngIf="checkable">
<mat-checkbox
#checkbox
[checked]="isChecked"
(change)="onChangeCheck(checkbox.checked, roomInfo)"
(click)="$event.stopPropagation()"
>
</mat-checkbox>
</dd>
</dl>
<!-- <span class="noti">1</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 {
RoomInfo,
UserInfoShort,
@ -7,13 +14,9 @@ import {
} from '@ucap-webmessenger/protocol-room';
import { NGXLogger } from 'ngx-logger';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
import { EventType } from '@ucap-webmessenger/protocol-event';
import { FileInfo, StickerInfo } from '@ucap-webmessenger/ui-chat';
import { FileType } from '@ucap-webmessenger/protocol-file';
import {
LoginResponse,
UserInfo
} from '@ucap-webmessenger/protocol-authentication';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { FormsModule } from '@angular/forms';
import { MatCheckbox } from '@angular/material';
@Component({
selector: 'ucap-room-list-item',
@ -29,6 +32,18 @@ export class ListItemComponent implements OnInit {
roomUserInfo: (RoomUserInfo | UserInfoShort)[];
@Input()
sessionVerinfo: VersionInfo2Response;
@Input()
checkable = false;
@Input()
isChecked = false;
@Input()
multiCheckable = true;
@Output()
checkRoom = new EventEmitter<{
isChecked: boolean;
roomInfo: RoomInfo;
}>();
imagePath: string;
defaultPath = 'assets/images/img_nophoto_50.png';
@ -95,4 +110,21 @@ export class ListItemComponent implements OnInit {
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 { MatInputModule } from '@angular/material/input';
import { ListItemComponent } from './components/list-item.component';
import { MatBadgeModule } from '@angular/material';
import { MatBadgeModule, MatCheckboxModule } from '@angular/material';
import { UCapUiModule } from '@ucap-webmessenger/ui';
@ -26,6 +26,7 @@ const SERVICES = [];
MatIconModule,
MatInputModule,
MatBadgeModule,
MatCheckboxModule,
UCapUiModule
],