import { Component, OnInit, ViewChild, OnDestroy, Inject } from '@angular/core'; import { MatPaginator, MatTableDataSource, MatSort } from '@angular/material'; import { FileInfo, FileDownloadInfo, FileType } from '@ucap-webmessenger/protocol-file'; import { Subscription, combineLatest } from 'rxjs'; import { Store, select } from '@ngrx/store'; import * as AppStore from '@app/store'; import * as EventStore from '@app/store/messenger/event'; import { tap, map, take, finalize } from 'rxjs/operators'; import { FileUtil, MimeUtil, DeviceType } from '@ucap-webmessenger/core'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type'; import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; import { NGXLogger } from 'ngx-logger'; import { FileDownloadItem } from '@ucap-webmessenger/api'; import { SnackBarService, DialogService, ConfirmDialogComponent, ConfirmDialogResult, ConfirmDialogData, DateOptions } from '@ucap-webmessenger/ui'; import { CommonApiService } from '@ucap-webmessenger/api-common'; import { EventType } from '@ucap-webmessenger/protocol-event'; import { CreateChatDialogComponent, CreateChatDialogResult, CreateChatDialogData } from '../../dialogs/chat/create-chat.dialog.component'; import { UserSelectDialogType, EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types'; import { RoomInfo } from '@ucap-webmessenger/protocol-room'; import { TranslateService } from '@ngx-translate/core'; export interface FileInfoTotal { info: FileInfo; checkInfo: FileDownloadInfo[]; fileDownloadItem: FileDownloadItem; } @Component({ selector: 'app-layout-chat-right-drawer-file-box', templateUrl: './file-box.component.html', styleUrls: ['./file-box.component.scss'] }) export class FileBoxComponent implements OnInit, OnDestroy { displayedColumns: string[] = ['check', 'name', 'sendDate']; dataSource = new MatTableDataSource(); fileInfoTotal: FileInfoTotal[]; fileInfoList: FileInfo[]; fileInfoListSubscription: Subscription; roomInfo: RoomInfo; selectedFile: FileInfoTotal; selectedFileList: FileInfoTotal[] = []; loginRes: LoginResponse; environmentsInfo: EnvironmentsInfo; currentTabIndex = 0; @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; @ViewChild(MatSort, { static: true }) sort: MatSort; constructor( private store: Store, private sessionStorageService: SessionStorageService, private commonApiService: CommonApiService, private translateService: TranslateService, private snackBarService: SnackBarService, @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, private dialogService: DialogService, private logger: NGXLogger ) { this.loginRes = this.sessionStorageService.get( KEY_LOGIN_RES_INFO ); this.environmentsInfo = this.sessionStorageService.get( KEY_ENVIRONMENTS_INFO ); } ngOnInit() { this.fileInfoListSubscription = combineLatest([ this.store.pipe(select(AppStore.MessengerSelector.RoomSelector.roomInfo)), this.store.pipe( select(AppStore.MessengerSelector.EventSelector.selectAllFileInfoList) ), this.store.pipe( select( AppStore.MessengerSelector.EventSelector.selectAllFileInfoCheckList ) ) ]) .pipe( tap(() => (this.fileInfoTotal = [])), tap(([roomInfo, fileInfoList, fileInfoCheckList]) => { this.roomInfo = roomInfo; this.fileInfoList = fileInfoList.filter(fileInfo => { if ( fileInfo.roomSeq === roomInfo.roomSeq && (fileInfo.type === FileType.File || fileInfo.type === FileType.Sound) ) { return true; } else { return false; } }); this.fileInfoList.map(fileInfo => { this.fileInfoTotal.push({ info: fileInfo, checkInfo: fileInfoCheckList.filter( checkInfo => checkInfo.seq === fileInfo.seq ), fileDownloadItem: new FileDownloadItem() }); }); this.onSelectedIndexChange(this.currentTabIndex); }) ) .subscribe(); this.dataSource.sortingDataAccessor = (item, property) => { switch (property) { case 'name': return item.info.name; case 'size': return item.info.size; case 'sendDate': return item.info.sendDate; default: return item.info[property]; } }; this.dataSource.sort = this.sort; this.dataSource.paginator = this.paginator; } ngOnDestroy(): void { if (!!this.fileInfoListSubscription) { this.fileInfoListSubscription.unsubscribe(); } } getExtention(name: string): string { return FileUtil.getExtension(name); } onSelectedIndexChange(index: number) { this.selectedFile = null; this.currentTabIndex = index; if (this.currentTabIndex === 0) { // Receive this.dataSource.data = this.fileInfoTotal.filter( fileInfo => fileInfo.info.senderSeq !== this.loginRes.userSeq ); } else { // send this.dataSource.data = this.fileInfoTotal.filter( fileInfo => fileInfo.info.senderSeq === this.loginRes.userSeq ); } } getCheckAllUser() { const data = this.dataSource .sortData(this.dataSource.data, this.sort) .filter((u, i) => i >= this.paginator.pageSize * this.paginator.pageIndex) .filter((u, i) => i < this.paginator.pageSize); if (data.length === 0) { return false; } if ( data.filter( dInfo => this.selectedFileList.filter( fileInfo => fileInfo.info.seq === dInfo.info.seq ).length === 0 ).length > 0 ) { return false; } else { return true; } } onCheckAllkUser(value: boolean) { const data = this.dataSource .sortData(this.dataSource.data, this.sort) .filter((u, i) => i >= this.paginator.pageSize * this.paginator.pageIndex) .filter((u, i) => i < this.paginator.pageSize); if (!!data && data.length > 0) { if (value) { this.selectedFileList.push( ...data.filter(dInfo => this.selectedFileList.filter( fileInfo => fileInfo.info.seq !== dInfo.info.seq ) ) ); } else { this.selectedFileList = this.selectedFileList.filter( fileInfo => !( data.filter(dInfo => dInfo.info.seq === fileInfo.info.seq) .length > 0 ) ); } } } getCheckUser(fileInfo: FileInfoTotal) { if (this.selectedFileList) { if ( this.selectedFileList.filter( info => info.info.seq === fileInfo.info.seq ).length > 0 ) { return true; } else { return false; } } else { return false; } } onCheckUser(value: boolean, fileInfo: FileInfoTotal) { if (value) { this.selectedFileList.push(fileInfo); } else { this.selectedFileList = this.selectedFileList.filter( info => info.info.seq !== fileInfo.info.seq ); } } onClickRow(row: FileInfoTotal) { this.selectedFile = row; } onClickDownload(fileInfo: FileInfoTotal) { this.commonApiService .fileTalkDownload({ userSeq: this.loginRes.userSeq, deviceType: this.environmentsInfo.deviceType, token: this.loginRes.tokenString, attachmentsSeq: fileInfo.info.seq, fileDownloadItem: fileInfo.fileDownloadItem }) .pipe( take(1), map(async rawBlob => { const mimeType = MimeUtil.getMimeFromExtension( FileUtil.getExtension(fileInfo.info.name) ); const blob = rawBlob.slice(0, rawBlob.size, mimeType); FileUtil.fromBlobToBuffer(blob) .then(buffer => { this.nativeService .saveFile(buffer, fileInfo.info.name, mimeType) .then(result => { if (!!result) { this.translateService .get('common.file.savedToPath', { v: result }) .pipe(take(1)) .subscribe(v => { this.snackBarService.open(v, '', { duration: 3000, verticalPosition: 'bottom' }); }); } else { this.translateService .get('common.file.failToSave') .pipe(take(1)) .subscribe(v => { this.snackBarService.open(v); }); } }) .catch(reason => { this.translateService .get('common.file.failToSave') .pipe(take(1)) .subscribe(v => { this.snackBarService.open(v); }); }); }) .catch(reason => { this.logger.error('download', reason); }); }), finalize(() => { setTimeout(() => { fileInfo.fileDownloadItem.downloadingProgress$ = undefined; }, 1000); }) ) .subscribe(); } onClickDownloadAll(): void { this.selectedFileList.forEach(fileInfo => { this.onClickDownload(fileInfo); }); } onClickOpenDownloadFolder(): void { this.nativeService .openDefaultDownloadFolder() .then(result => { if (!!result) { } else { throw new Error('response Error'); } }) .catch(reason => { this.logger.error(reason); }); } onClickForwardMe(fileInfo: FileInfoTotal) { this.store.dispatch( EventStore.forward({ senderSeq: this.loginRes.userSeq, req: { roomSeq: '-999', eventType: EventType.File, sentMessage: fileInfo.info.sentMessage }, trgtUserSeqs: [this.loginRes.talkWithMeBotSeq] }) ); } onClickForward(fileInfo: FileInfoTotal) { this.translateService .get('chat.forwardFileTo') .pipe(take(1)) .subscribe(async v => { const result = await this.dialogService.open< CreateChatDialogComponent, CreateChatDialogData, CreateChatDialogResult >(CreateChatDialogComponent, { width: '600px', data: { type: UserSelectDialogType.MessageForward, title: v, 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: EventType.File, sentMessage: fileInfo.info.sentMessage }, trgtUserSeqs: userSeqs, trgtRoomSeq: roomSeq }) ); } } }); } onClickDelete(fileInfo: FileInfoTotal) { this.translateService .get('chat.confirmDeleteFile') .pipe(take(1)) .subscribe(async v => { const result = await this.dialogService.open< ConfirmDialogComponent, ConfirmDialogData, ConfirmDialogResult >(ConfirmDialogComponent, { width: '400px', data: { title: 'Delete', html: v } }); if (!!result && !!result.choice && result.choice) { this.store.dispatch( EventStore.del({ roomSeq: this.roomInfo.roomSeq, eventSeq: fileInfo.info.eventSeq }) ); this.fileInfoTotal = this.fileInfoTotal.filter( fInfo => fInfo.info.seq !== fileInfo.info.seq ); this.onSelectedIndexChange(this.currentTabIndex); } }); } get fileRetentionPeriodOptions(): DateOptions { return { manipulate: { amount: this.loginRes.fileRetentionPeriod, unit: 'days' } }; } }