import { Component, OnInit, Inject, ViewChild } from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA, MatSelectionList, MatDrawer } from '@angular/material'; import { map, catchError, take } from 'rxjs/operators'; import { DialogService, ConfirmDialogComponent, ConfirmDialogData, ConfirmDialogResult, SnackBarService, AlertDialogComponent, AlertDialogResult, AlertDialogData } from '@ucap-webmessenger/ui'; import { DetailResponse, MessageType, DetailContent, DetailReceiver, ContentType, MessageDetailInfo, MessageApiService, RetrieveResourceFileRequest, CancelRequest } from '@ucap-webmessenger/api-message'; import { DeviceType, MimeUtil, FileUtil } from '@ucap-webmessenger/core'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { NGXLogger } from 'ngx-logger'; import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; import { MessageStatusCode } from '@ucap-webmessenger/api'; import { of } from 'rxjs'; export interface MessageDetailDialogData { detail: DetailResponse; loginRes: LoginResponse; } export interface MessageDetailDialogResult { returnType: string; messageInfo?: MessageDetailInfo; cancelUserSeqs?: number[]; } export interface DownloadQueueForMessage extends DetailContent { downloadType: string; } @Component({ selector: 'app-layout-messenger-message-detail', templateUrl: './message-detail.dialog.component.html', styleUrls: ['./message-detail.dialog.component.scss'] }) export class MessageDetailDialogComponent implements OnInit { messageDetail: DetailResponse; messageInfo: MessageDetailInfo; contents: DetailContent[] = []; attachFile: DetailContent[] = []; receivers: DetailReceiver[] = []; isExpiredAttachFile = true; MessageType = MessageType; ContentType = ContentType; downloadProgress = false; downloadQueue: DownloadQueueForMessage[] = []; downloadFail: DownloadQueueForMessage[] = []; @ViewChild('rightDrawer', { static: true }) rightDrawer: MatDrawer; @ViewChild('unReadUsers', { static: false }) unReadUsers: MatSelectionList; constructor( public dialogRef: MatDialogRef< MessageDetailDialogData, MessageDetailDialogResult >, @Inject(MAT_DIALOG_DATA) public data: MessageDetailDialogData, @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, private messageApiService: MessageApiService, private snackBarService: SnackBarService, private logger: NGXLogger, private dialogService: DialogService ) {} ngOnInit(): void { this.messageDetail = this.data.detail; this.messageInfo = this.messageDetail.msgInfo; if ( !!this.messageDetail.contents && this.messageDetail.contents.length > 0 ) { this.messageDetail.contents.forEach(cont => { if (cont.resType !== ContentType.AttachFile) { this.contents.push(cont); } else if (cont.resType === ContentType.AttachFile) { if (cont.activeYn) { this.isExpiredAttachFile = false; } this.attachFile.push(cont); } }); } // contents 내 이미지 Thumnail 파일 정보 수집. this.getThumbImage(); this.receivers = this.messageDetail.recvList.sort((a, b) => a.userName < b.userName ? -1 : a.userName > b.userName ? 1 : 0 ); } getSendReceiverNames(): string { if (this.messageInfo.type === MessageType.Receive) { return this.messageInfo.sendUserName; } else { return this.receivers.map(user => user.userName).join(','); } } getReadUserCount(readYn: boolean): number { return this.receivers.filter(user => user.readYn === readYn).length; } getFileStatusIcon(file: DetailContent) { const downloading = this.downloadQueue.filter(dq => dq.resSeq === file.resSeq).length > 0; const error = this.downloadFail.filter(df => df.resSeq === file.resSeq).length > 0; if (error) { return 'mdi-window-close'; } else if (downloading) { return ['mdi-spin', 'mdi-loading']; } else { return 'mdi-attachment'; } } getThumbImage(): void { this.contents.forEach(content => { if (content.resType === ContentType.Image) { this.messageApiService .retrieveResourceFile({ userSeq: this.data.loginRes.userSeq, deviceType: DeviceType.PC, tokenKey: this.data.loginRes.tokenString, type: this.messageInfo.type, msgId: this.messageInfo.msgId, resUrl: content.thumbnailUrl } as RetrieveResourceFileRequest) .pipe( take(1), map(async rawBlob => { const reader = new FileReader(); reader.readAsDataURL(rawBlob); reader.onloadend = () => { content.imageSrc = reader.result; }; }) ) .subscribe(); } }); } // /** // * @deprecated // */ // downloadAttachFile(attachFile: DetailContent): void { // this.messageApiService // .retrieveResourceFile({ // userSeq: this.data.loginRes.userSeq, // deviceType: DeviceType.PC, // tokenKey: this.data.loginRes.tokenString, // type: this.messageInfo.type, // msgId: this.messageInfo.msgId, // resUrl: attachFile.resUrl // } as RetrieveResourceFileRequest) // .pipe( // take(1), // map(async rawBlob => { // const mimeType = MimeUtil.getMimeFromExtension( // FileUtil.getExtension(attachFile.resContent) // ); // const blob = rawBlob.slice(0, rawBlob.size, mimeType); // FileUtil.fromBlobToBuffer(blob) // .then(buffer => { // this.nativeService // .saveFile(buffer, attachFile.resContent, mimeType) // .then(result => { // if (!!result) { // if (this.downloadFail.length > 0) { // this.downloadFail = this.downloadFail.filter( // df => df.resSeq !== attachFile.resSeq // ); // } // this.snackBarService.open( // `파일이 경로[${result}]에 저장되었습니다.`, // '', // { // duration: 3000, // verticalPosition: 'bottom' // } // ); // } else { // this.snackBarService.open('파일 저장에 실패하였습니다.'); // } // }) // .catch(reason => { // this.snackBarService.open('파일 저장에 실패하였습니다.'); // }); // }) // .catch(reason => { // this.logger.error('download', reason); // }); // }) // ) // .subscribe(); // } downloadAttachFileSingle(attachFile: DetailContent): void { if (!this.downloadProgress) { this.downloadProgress = true; this.downloadQueue = [{ ...attachFile, downloadType: 'SINGLE' }]; this.downloadFail = []; if (!!this.downloadQueue && this.downloadQueue.length > 0) { this.downloadAttachFileByQueue(); } } else { if ( this.downloadQueue.filter(dq => dq.resSeq === attachFile.resSeq) .length === 0 ) { this.downloadQueue.push({ ...attachFile, downloadType: 'SINGLE' }); } } } downloadAttachFileAll(): void { if (!this.downloadProgress) { this.downloadProgress = true; this.downloadQueue = []; this.downloadFail = []; this.attachFile.forEach(file => this.downloadQueue.push({ ...file, downloadType: '' }) ); if (!!this.downloadQueue && this.downloadQueue.length > 0) { this.downloadAttachFileByQueue(); } } else { this.dialogService.open< AlertDialogComponent, AlertDialogData, AlertDialogResult >(AlertDialogComponent, { data: { title: '', html: `다운로드가 진행중입니다.` } }); } } downloadAttachFileByQueue(): void { const attachFile = this.downloadQueue[0]; this.messageApiService .retrieveResourceFile({ userSeq: this.data.loginRes.userSeq, deviceType: DeviceType.PC, tokenKey: this.data.loginRes.tokenString, type: this.messageInfo.type, msgId: this.messageInfo.msgId, resUrl: attachFile.resUrl } as RetrieveResourceFileRequest) .pipe( take(1), map(async rawBlob => { const mimeType = MimeUtil.getMimeFromExtension( FileUtil.getExtension(attachFile.resContent) ); const blob = rawBlob.slice(0, rawBlob.size, mimeType); FileUtil.fromBlobToBuffer(blob) .then(buffer => { this.nativeService .saveFile(buffer, attachFile.resContent, mimeType) .then(result => { if (!!result) { if ( !!attachFile.downloadType && attachFile.downloadType === 'SINGLE' ) { attachFile.downloadType = result; } if (this.downloadQueue.length > 1) { this.downloadQueue = this.downloadQueue.slice(1); } else { this.downloadQueue = []; } } else { throw new Error('response Error'); } }) .catch(reason => { this.downloadFail.push(this.downloadQueue[0]); if (this.downloadQueue.length > 1) { this.downloadQueue = this.downloadQueue.slice(1); } else { this.downloadQueue = []; } }); }) .catch(reason => { this.downloadFail.push(this.downloadQueue[0]); if (this.downloadQueue.length > 1) { this.downloadQueue = this.downloadQueue.slice(1); } else { this.downloadQueue = []; } }) .finally(() => { if (this.downloadQueue.length > 0) { // 재귀 this.downloadAttachFileByQueue(); } else { if (this.downloadFail.length > 0) { // 일부 혹은 전부 실패. let errMsg = ''; if ( !!attachFile.downloadType && attachFile.downloadType === 'SINGLE' ) { // single :: fail errMsg = '파일 저장에 실패하였습니다.'; } else { // all errMsg = '일부 저장중 오류가 발생하였습니다.'; } this.snackBarService.open(errMsg, '확인', { duration: 8000, verticalPosition: 'bottom' }); } else { // 성공종료. if ( !!attachFile.downloadType && attachFile.downloadType.length > 0 ) { // single :: success this.snackBarService.open( `파일이 경로[${attachFile.downloadType}]에 저장되었습니다.`, '', { duration: 3000, verticalPosition: 'bottom' } ); } else { // all this.snackBarService.open('모두 저장하였습니다.', '', { duration: 3000, verticalPosition: 'bottom' }); } } this.downloadProgress = false; } }); }) ) .subscribe(); } async onClickMessageMenu(menuType: string) { switch (menuType) { case 'MESSAGE_READ': { this.rightDrawer.open(); } break; case 'MESSAGE_CANCEL': { const result = await this.dialogService.open< ConfirmDialogComponent, ConfirmDialogData, ConfirmDialogResult >(ConfirmDialogComponent, { data: { title: '발송취소', html: `예약발송을 취소 하시겠습니까?
취소하면 목록에서도 영구 삭제됩니다.` } }); if (!!result && !!result.choice && result.choice) { this.dialogRef.close({ returnType: 'CANCEL_RESERVATION', messageInfo: this.messageInfo }); } } break; case 'MESSAGE_DEL': { const result = await this.dialogService.open< ConfirmDialogComponent, ConfirmDialogData, ConfirmDialogResult >(ConfirmDialogComponent, { data: { title: '삭제', message: '선택한 쪽지를 삭제하시겠습니까?' } }); if (!!result && !!result.choice && result.choice) { this.dialogRef.close({ returnType: 'DEL', messageInfo: this.messageInfo }); } } break; } } async cancelSendMessageForUsers() { if ( !!this.unReadUsers && this.unReadUsers.selectedOptions.selected.length > 0 ) { const result = await this.dialogService.open< ConfirmDialogComponent, ConfirmDialogData, ConfirmDialogResult >(ConfirmDialogComponent, { data: { title: '발송 취소', message: '발송 취소를 하시면 받는 사람의 쪽지함에서 쪽지가 삭제됩니다.' } }); if (!!result && !!result.choice && result.choice) { const cancelUserSeqs: number[] = []; const recvUserList: { userSeq: number }[] = []; this.unReadUsers.selectedOptions.selected.forEach(selected => { cancelUserSeqs.push(selected.value); recvUserList.push({ userSeq: selected.value }); }); this.messageApiService .cancelMessage({ userSeq: this.data.loginRes.userSeq, deviceType: DeviceType.PC, tokenKey: this.data.loginRes.tokenString, type: this.messageInfo.type, msgId: this.messageInfo.msgId, recvUserList } as CancelRequest) .pipe( map(async res => { if (res.responseCode === MessageStatusCode.Success) { this.receivers = this.receivers.filter( user => cancelUserSeqs.indexOf(user.userSeq) < 0 ); this.rightDrawer.close(); } else { this.logger.error('message cancel user Error!'); } }), catchError(error => of(this.logger.error(error))) ) .subscribe(); } } } onClickConfirm(): void { this.dialogRef.close({ returnType: 'CLOSE' }); } }