diff --git a/electron-projects/ucap-webmessenger-electron/src/index.ts b/electron-projects/ucap-webmessenger-electron/src/index.ts index f963e4ee..6765a305 100644 --- a/electron-projects/ucap-webmessenger-electron/src/index.ts +++ b/electron-projects/ucap-webmessenger-electron/src/index.ts @@ -513,15 +513,18 @@ ipcMain.on( const buffer: Buffer = args[0]; const fileName: string = args[1]; const mimeType: string = args[2]; + const customSavePath: string = args[3]; let savePath: string = path.join( - !!args[3] - ? args[3] - : !!appStorage.downloadPath + !!appStorage.downloadPath ? appStorage.downloadPath : app.getPath('downloads'), fileName ); - savePath = await FileUtil.uniqueFileName(savePath); + if (!!customSavePath) { + savePath = customSavePath; + } else { + savePath = await FileUtil.uniqueFileName(savePath); + } fse.writeFile(savePath, buffer, err => { if (!err) { @@ -594,6 +597,20 @@ ipcMain.on( } ); +ipcMain.on( + FileChannel.SelectSaveFilePath, + (event: IpcMainEvent, ...args: any[]) => { + dialog + .showSaveDialog({ defaultPath: args[0] }) + .then(obj => { + event.returnValue = obj.filePath; + }) + .catch(obj => { + event.returnValue = undefined; + }); + } +); + ipcMain.on( IdleStateChannel.StartCheck, (event: IpcMainEvent, ...args: any[]) => { diff --git a/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts index ec45d927..723a18f3 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts @@ -105,11 +105,17 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy { } ); } else { - this.snackBarService.open('파일 저장에 실패하였습니다.'); + this.snackBarService.open( + '파일 저장에 실패하였습니다.', + '확인' + ); } }) .catch(reason => { - this.snackBarService.open('파일 저장에 실패하였습니다.'); + this.snackBarService.open( + '파일 저장에 실패하였습니다.', + '확인' + ); }); }) .catch(reason => { diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts index 2f1f637e..5ffa719e 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts @@ -5,7 +5,8 @@ import { ViewChild, AfterViewInit, Output, - EventEmitter + EventEmitter, + Inject } from '@angular/core'; import { ucapAnimations, @@ -58,7 +59,7 @@ import { KEY_STICKER_HISTORY } from '@app/types'; import { RoomInfo, UserInfo, RoomType } from '@ucap-webmessenger/protocol-room'; -import { tap, take, map, catchError } from 'rxjs/operators'; +import { tap, take, map, catchError, finalize } from 'rxjs/operators'; import { FileInfo, FormComponent as UCapUiChatFormComponent @@ -70,7 +71,7 @@ import { MatSnackBarRef, SimpleSnackBar } from '@angular/material'; -import { FileUploadItem } from '@ucap-webmessenger/api'; +import { FileUploadItem, FileDownloadItem } from '@ucap-webmessenger/api'; import { CommonApiService, FileTalkSaveRequest, @@ -88,7 +89,7 @@ import { FileViewerDialogData, FileViewerDialogResult } from '@app/layouts/common/dialogs/file-viewer.dialog.component'; -import { FileUtil, StickerFilesInfo } from '@ucap-webmessenger/core'; +import { FileUtil, StickerFilesInfo, MimeUtil } from '@ucap-webmessenger/core'; import { PerfectScrollbarComponent } from 'ngx-perfect-scrollbar'; import { StatusCode } from '@ucap-webmessenger/api'; import { @@ -107,6 +108,7 @@ import { MassDetailComponent, MassDetailDialogData } from '../dialogs/chat/mass-detail.component'; +import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; @Component({ selector: 'app-layout-messenger-messages', @@ -204,6 +206,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { private clipboardService: ClipboardService, private dialogService: DialogService, private snackBarService: SnackBarService, + @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, private logger: NGXLogger ) { this.sessionVerInfo = this.sessionStorageService.get( @@ -844,8 +847,29 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { } /** File Save, Save As */ - onSave(value: { fileInfo: FileInfo; type: string }) { + onSave(value: { + fileInfo: FileEventJson; + fileDownloadItem: FileDownloadItem; + type: string; + }) { this.logger.debug('fileSave', value); + if (value.type === 'saveAs') { + this.nativeService + .selectSaveFilePath(value.fileInfo.fileName) + .then(result => { + console.log(result); + if (!!result && result.length > 0) { + this.saveFile(value, result); + } else { + this.snackBarService.open('저장경로 지정에 실패하였습니다.', '확인'); + } + }) + .catch(reason => { + this.snackBarService.open('저장경로 지정에 실패하였습니다.', '확인'); + }); + } else { + this.saveFile(value); + } } onFileDragEnter(items: DataTransferItemList) { @@ -861,6 +885,65 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { this.logger.debug('onFileDragLeave'); } + saveFile( + value: { + fileInfo: FileEventJson; + fileDownloadItem: FileDownloadItem; + type: string; + }, + savePath?: string + ) { + this.commonApiService + .fileTalkDownload({ + userSeq: this.loginRes.userSeq, + deviceType: this.environmentsInfo.deviceType, + token: this.loginRes.tokenString, + attachmentsSeq: value.fileInfo.attachmentSeq, + fileDownloadItem: value.fileDownloadItem + }) + .pipe( + take(1), + map(async rawBlob => { + const mimeType = MimeUtil.getMimeFromExtension( + FileUtil.getExtension(value.fileInfo.fileName) + ); + const blob = rawBlob.slice(0, rawBlob.size, mimeType); + + FileUtil.fromBlobToBuffer(blob) + .then(buffer => { + this.nativeService + .saveFile(buffer, value.fileInfo.fileName, mimeType, savePath) + .then(result => { + if (!!result) { + this.snackBarService.open( + `파일이 경로[${result}]에 저장되었습니다.`, + '', + { + duration: 3000, + verticalPosition: 'bottom' + } + ); + } else { + this.snackBarService.open('파일 저장에 실패하였습니다.', '확인'); + } + }) + .catch(reason => { + this.snackBarService.open('파일 저장에 실패하였습니다.', '확인'); + }); + }) + .catch(reason => { + this.logger.error('download', reason); + }); + }), + finalize(() => { + setTimeout(() => { + value.fileDownloadItem.downloadingProgress$ = undefined; + }, 1000); + }) + ) + .subscribe(); + } + async onFileSelected(fileUploadItems: FileUploadItem[]) { this.logger.debug('onFileSelected', fileUploadItems); this.clearView(); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.ts index d06307a5..577b996d 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.ts @@ -239,11 +239,17 @@ export class AlbumBoxComponent implements OnInit, OnDestroy { } ); } else { - this.snackBarService.open('파일 저장에 실패하였습니다.'); + this.snackBarService.open( + '파일 저장에 실패하였습니다.', + '확인' + ); } }) .catch(reason => { - this.snackBarService.open('파일 저장에 실패하였습니다.'); + this.snackBarService.open( + '파일 저장에 실패하였습니다.', + '확인' + ); }); }) .catch(reason => { diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.ts index d5b2aa69..8210cb7f 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.ts @@ -275,11 +275,17 @@ export class FileBoxComponent implements OnInit, OnDestroy { } ); } else { - this.snackBarService.open('파일 저장에 실패하였습니다.'); + this.snackBarService.open( + '파일 저장에 실패하였습니다.', + '확인' + ); } }) .catch(reason => { - this.snackBarService.open('파일 저장에 실패하였습니다.'); + this.snackBarService.open( + '파일 저장에 실패하였습니다.', + '확인' + ); }); }) .catch(reason => { diff --git a/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts b/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts index b9ea0700..c3a93584 100644 --- a/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts +++ b/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts @@ -68,7 +68,7 @@ export class AppAuthenticationService { ...environment.productConfig.defaultSettings.chat, downloadPath: `${await this.nativeService.getPath( 'documents' - )}/LG UCAP downloads` + )}/Messenger downloads` } } }; diff --git a/projects/ucap-webmessenger-native-browser/src/lib/services/browser-native.service.ts b/projects/ucap-webmessenger-native-browser/src/lib/services/browser-native.service.ts index f4ca0f3f..1f4abbae 100644 --- a/projects/ucap-webmessenger-native-browser/src/lib/services/browser-native.service.ts +++ b/projects/ucap-webmessenger-native-browser/src/lib/services/browser-native.service.ts @@ -185,6 +185,12 @@ export class BrowserNativeService extends NativeService { }); } + selectSaveFilePath(defaultPath?: string): Promise { + return new Promise((resolve, reject) => { + resolve(''); + }); + } + windowStateChanged(): Observable { return new Observable(subscriber => { try { diff --git a/projects/ucap-webmessenger-native-electron/src/lib/services/electron-native.service.ts b/projects/ucap-webmessenger-native-electron/src/lib/services/electron-native.service.ts index 525472b4..7dc95eb0 100644 --- a/projects/ucap-webmessenger-native-electron/src/lib/services/electron-native.service.ts +++ b/projects/ucap-webmessenger-native-electron/src/lib/services/electron-native.service.ts @@ -304,6 +304,18 @@ export class ElectronNativeService implements NativeService { }); } + selectSaveFilePath(defaultPath?: string): Promise { + return new Promise((resolve, reject) => { + try { + resolve( + this.ipcRenderer.sendSync(FileChannel.SelectSaveFilePath, defaultPath) + ); + } catch (error) { + reject(error); + } + }); + } + windowStateChanged(): Observable { if (!this.windowStateChangedSubject) { this.windowStateChangedSubject = new Subject(); diff --git a/projects/ucap-webmessenger-native-electron/src/lib/types/channel.type.ts b/projects/ucap-webmessenger-native-electron/src/lib/types/channel.type.ts index 5780a9ce..18203a06 100644 --- a/projects/ucap-webmessenger-native-electron/src/lib/types/channel.type.ts +++ b/projects/ucap-webmessenger-native-electron/src/lib/types/channel.type.ts @@ -35,7 +35,8 @@ export enum FileChannel { SaveFile = 'UCAP::file::saveFile', ReadFile = 'UCAP::file::readFile', GetPath = 'UCAP::file::getPath', - SelectDirectory = 'UCAP::file::selectDirectory' + SelectDirectory = 'UCAP::file::selectDirectory', + SelectSaveFilePath = 'UCAP::file::SelectSaveFilePath' } export enum WindowStateChannel { diff --git a/projects/ucap-webmessenger-native/src/lib/services/native.service.ts b/projects/ucap-webmessenger-native/src/lib/services/native.service.ts index d5171bf8..d9b9f5ab 100644 --- a/projects/ucap-webmessenger-native/src/lib/services/native.service.ts +++ b/projects/ucap-webmessenger-native/src/lib/services/native.service.ts @@ -58,6 +58,7 @@ export abstract class NativeService { abstract openTargetItem(filePath?: string): Promise; abstract getPath(name: NativePathName): Promise; abstract selectDirectory(): Promise; + abstract selectSaveFilePath(defaultPath?: string): Promise; abstract windowStateChanged(): Observable; abstract windowClose(): void; diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/attach-file.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/attach-file.component.html index 0b13d30c..257bac77 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/attach-file.component.html +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/attach-file.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/attach-file.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/attach-file.component.ts index 1f54e3b3..0162cd8b 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/attach-file.component.ts +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/attach-file.component.ts @@ -15,6 +15,8 @@ export class AttachFileComponent implements OnInit { @Output() save = new EventEmitter(); + @Output() + openViewer = new EventEmitter(); constructor(private logger: NGXLogger) {} @@ -26,4 +28,7 @@ export class AttachFileComponent implements OnInit { onClickSaveAs() { this.save.emit('saveAs'); } + onClickOpenViewer() { + this.openViewer.emit(); + } } diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/file.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/file.component.html index 1bd22a2b..9a2012b9 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/file.component.html +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/file.component.html @@ -6,7 +6,7 @@ *ngSwitchCase="FileType.File" [fileInfo]="fileInfo" [expired]="getExpiredFile()" - (click)="onClickFileViewer(fileInfo)" + (openViewer)="onClickFileViewer(fileInfo)" (save)="onSave($event)" > @@ -14,7 +14,7 @@ *ngSwitchCase="FileType.Sound" [fileInfo]="fileInfo" [expired]="getExpiredFile()" - (click)="onClickFileViewer(fileInfo)" + (openViewer)="onClickFileViewer(fileInfo)" (save)="onSave($event)" > @@ -28,7 +28,8 @@ *ngSwitchCase="FileType.Video" [fileInfo]="fileInfo" [expired]="getExpiredFile()" - (click)="onClickFileViewer(fileInfo)" + (openViewer)="onClickFileViewer(fileInfo)" + (save)="onSave($event)" > (); + save = new EventEmitter<{ + fileInfo: FileEventJson; + fileDownloadItem: FileDownloadItem; + type: string; + }>(); @Output() fileViewer = new EventEmitter(); fileInfo?: FileEventJson; + fileDownloadItem: FileDownloadItem; errorMessage?: string; FileType = FileType; @@ -37,6 +42,8 @@ export class FileComponent implements OnInit { this.errorMessage = this.message.sentMessageJson.errorMessage || '[Error] System Error!!'; } + + this.fileDownloadItem = new FileDownloadItem(); } getExpiredFile() { @@ -58,7 +65,11 @@ export class FileComponent implements OnInit { onSave(value: string) { if (!this.getExpiredFile()) { - this.save.emit({ fileInfo: this.fileInfo, type: value }); + this.save.emit({ + fileInfo: this.fileInfo, + fileDownloadItem: this.fileDownloadItem, + type: value + }); } } } diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.html index c3b4c8aa..b9013e12 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.html +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.ts index c8fb8879..78e8aaee 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.ts +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.ts @@ -15,6 +15,8 @@ export class VideoComponent implements OnInit { @Output() save = new EventEmitter(); + @Output() + openViewer = new EventEmitter(); constructor(private logger: NGXLogger) {} @@ -26,4 +28,7 @@ export class VideoComponent implements OnInit { onClickSaveAs() { this.save.emit('saveAs'); } + onClickOpenViewer() { + this.openViewer.emit(); + } } diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.ts index 1ff0e594..fac5f2d6 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.ts +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/messages.component.ts @@ -14,6 +14,7 @@ import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; import { FileInfo } from '../models/file-info.json'; import { DatePipe } from '@angular/common'; import moment from 'moment'; +import { FileDownloadItem } from '@ucap-webmessenger/api'; @Component({ selector: 'ucap-chat-messages', @@ -67,7 +68,11 @@ export class MessagesComponent implements OnInit { @Output() fileViewer = new EventEmitter(); @Output() - save = new EventEmitter<{ fileInfo: FileInfo; type: string }>(); + save = new EventEmitter<{ + fileInfo: FileEventJson; + fileDownloadItem: FileDownloadItem; + type: string; + }>(); @Output() contextMenu = new EventEmitter<{ event: MouseEvent; @@ -233,7 +238,11 @@ export class MessagesComponent implements OnInit { } /** [Event] Attach File Save & Save As */ - onSave(value: { fileInfo: FileInfo; type: string }) { + onSave(value: { + fileInfo: FileEventJson; + fileDownloadItem: FileDownloadItem; + type: string; + }) { this.save.emit(value); }