This commit is contained in:
병준 박 2020-01-03 16:18:39 +09:00
commit 4bb3a7dece
18 changed files with 214 additions and 26 deletions

View File

@ -513,15 +513,18 @@ ipcMain.on(
const buffer: Buffer = args[0]; const buffer: Buffer = args[0];
const fileName: string = args[1]; const fileName: string = args[1];
const mimeType: string = args[2]; const mimeType: string = args[2];
const customSavePath: string = args[3];
let savePath: string = path.join( let savePath: string = path.join(
!!args[3] !!appStorage.downloadPath
? args[3]
: !!appStorage.downloadPath
? appStorage.downloadPath ? appStorage.downloadPath
: app.getPath('downloads'), : app.getPath('downloads'),
fileName fileName
); );
if (!!customSavePath) {
savePath = customSavePath;
} else {
savePath = await FileUtil.uniqueFileName(savePath); savePath = await FileUtil.uniqueFileName(savePath);
}
fse.writeFile(savePath, buffer, err => { fse.writeFile(savePath, buffer, err => {
if (!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( ipcMain.on(
IdleStateChannel.StartCheck, IdleStateChannel.StartCheck,
(event: IpcMainEvent, ...args: any[]) => { (event: IpcMainEvent, ...args: any[]) => {

View File

@ -68,6 +68,14 @@
(openProfile)="onClickOpenProfile($event)" (openProfile)="onClickOpenProfile($event)"
(click)="onToggleUser(userInfo)" (click)="onToggleUser(userInfo)"
(contextmenu)="onContextMenuOrgUser($event, userInfo)" (contextmenu)="onContextMenuOrgUser($event, userInfo)"
[matTooltip]="
userInfo.companyName +
' / ' +
userInfo.lineNumber +
' / ' +
userInfo.hpNumber
"
matTooltipPosition="after"
> >
</ucap-profile-user-list-item> </ucap-profile-user-list-item>
</cdk-virtual-scroll-viewport> </cdk-virtual-scroll-viewport>
@ -90,6 +98,14 @@
(openProfile)="onClickOpenProfile($event)" (openProfile)="onClickOpenProfile($event)"
(click)="onToggleUser(userInfo)" (click)="onToggleUser(userInfo)"
(contextmenu)="onContextMenuOrgUser($event, userInfo)" (contextmenu)="onContextMenuOrgUser($event, userInfo)"
[matTooltip]="
userInfo.companyName +
' / ' +
userInfo.lineNumber +
' / ' +
userInfo.hpNumber
"
matTooltipPosition="after"
> >
</ucap-profile-user-list-item> </ucap-profile-user-list-item>
</cdk-virtual-scroll-viewport> </cdk-virtual-scroll-viewport>

View File

@ -5,7 +5,8 @@ import {
ViewChild, ViewChild,
AfterViewInit, AfterViewInit,
Output, Output,
EventEmitter EventEmitter,
Inject
} from '@angular/core'; } from '@angular/core';
import { import {
ucapAnimations, ucapAnimations,
@ -58,7 +59,7 @@ import {
KEY_STICKER_HISTORY KEY_STICKER_HISTORY
} from '@app/types'; } from '@app/types';
import { RoomInfo, UserInfo, RoomType } from '@ucap-webmessenger/protocol-room'; 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 { import {
FileInfo, FileInfo,
FormComponent as UCapUiChatFormComponent FormComponent as UCapUiChatFormComponent
@ -70,7 +71,7 @@ import {
MatSnackBarRef, MatSnackBarRef,
SimpleSnackBar SimpleSnackBar
} from '@angular/material'; } from '@angular/material';
import { FileUploadItem } from '@ucap-webmessenger/api'; import { FileUploadItem, FileDownloadItem } from '@ucap-webmessenger/api';
import { import {
CommonApiService, CommonApiService,
FileTalkSaveRequest, FileTalkSaveRequest,
@ -88,7 +89,7 @@ import {
FileViewerDialogData, FileViewerDialogData,
FileViewerDialogResult FileViewerDialogResult
} from '@app/layouts/common/dialogs/file-viewer.dialog.component'; } 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 { PerfectScrollbarComponent } from 'ngx-perfect-scrollbar';
import { StatusCode } from '@ucap-webmessenger/api'; import { StatusCode } from '@ucap-webmessenger/api';
import { import {
@ -107,6 +108,7 @@ import {
MassDetailComponent, MassDetailComponent,
MassDetailDialogData MassDetailDialogData
} from '../dialogs/chat/mass-detail.component'; } from '../dialogs/chat/mass-detail.component';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
@Component({ @Component({
selector: 'app-layout-messenger-messages', selector: 'app-layout-messenger-messages',
@ -204,6 +206,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
private clipboardService: ClipboardService, private clipboardService: ClipboardService,
private dialogService: DialogService, private dialogService: DialogService,
private snackBarService: SnackBarService, private snackBarService: SnackBarService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private logger: NGXLogger private logger: NGXLogger
) { ) {
this.sessionVerInfo = this.sessionStorageService.get<VersionInfo2Response>( this.sessionVerInfo = this.sessionStorageService.get<VersionInfo2Response>(
@ -844,8 +847,29 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
} }
/** File Save, Save As */ /** File Save, Save As */
onSave(value: { fileInfo: FileInfo; type: string }) { onSave(value: {
fileInfo: FileEventJson;
fileDownloadItem: FileDownloadItem;
type: string;
}) {
this.logger.debug('fileSave', value); 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) { onFileDragEnter(items: DataTransferItemList) {
@ -861,6 +885,65 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
this.logger.debug('onFileDragLeave'); 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[]) { async onFileSelected(fileUploadItems: FileUploadItem[]) {
this.logger.debug('onFileSelected', fileUploadItems); this.logger.debug('onFileSelected', fileUploadItems);
this.clearView(); this.clearView();

View File

@ -239,11 +239,17 @@ export class AlbumBoxComponent implements OnInit, OnDestroy {
} }
); );
} else { } else {
this.snackBarService.open('파일 저장에 실패하였습니다.'); this.snackBarService.open(
'파일 저장에 실패하였습니다.',
'확인'
);
} }
}) })
.catch(reason => { .catch(reason => {
this.snackBarService.open('파일 저장에 실패하였습니다.'); this.snackBarService.open(
'파일 저장에 실패하였습니다.',
'확인'
);
}); });
}) })
.catch(reason => { .catch(reason => {

View File

@ -275,11 +275,17 @@ export class FileBoxComponent implements OnInit, OnDestroy {
} }
); );
} else { } else {
this.snackBarService.open('파일 저장에 실패하였습니다.'); this.snackBarService.open(
'파일 저장에 실패하였습니다.',
'확인'
);
} }
}) })
.catch(reason => { .catch(reason => {
this.snackBarService.open('파일 저장에 실패하였습니다.'); this.snackBarService.open(
'파일 저장에 실패하였습니다.',
'확인'
);
}); });
}) })
.catch(reason => { .catch(reason => {

View File

@ -68,7 +68,7 @@ export class AppAuthenticationService {
...environment.productConfig.defaultSettings.chat, ...environment.productConfig.defaultSettings.chat,
downloadPath: `${await this.nativeService.getPath( downloadPath: `${await this.nativeService.getPath(
'documents' 'documents'
)}/LG UCAP downloads` )}/Messenger downloads`
} }
} }
}; };

View File

@ -185,6 +185,12 @@ export class BrowserNativeService extends NativeService {
}); });
} }
selectSaveFilePath(defaultPath?: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
resolve('');
});
}
windowStateChanged(): Observable<WindowState> { windowStateChanged(): Observable<WindowState> {
return new Observable<WindowState>(subscriber => { return new Observable<WindowState>(subscriber => {
try { try {

View File

@ -304,6 +304,18 @@ export class ElectronNativeService implements NativeService {
}); });
} }
selectSaveFilePath(defaultPath?: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
try {
resolve(
this.ipcRenderer.sendSync(FileChannel.SelectSaveFilePath, defaultPath)
);
} catch (error) {
reject(error);
}
});
}
windowStateChanged(): Observable<WindowState> { windowStateChanged(): Observable<WindowState> {
if (!this.windowStateChangedSubject) { if (!this.windowStateChangedSubject) {
this.windowStateChangedSubject = new Subject<WindowState>(); this.windowStateChangedSubject = new Subject<WindowState>();

View File

@ -35,7 +35,8 @@ export enum FileChannel {
SaveFile = 'UCAP::file::saveFile', SaveFile = 'UCAP::file::saveFile',
ReadFile = 'UCAP::file::readFile', ReadFile = 'UCAP::file::readFile',
GetPath = 'UCAP::file::getPath', GetPath = 'UCAP::file::getPath',
SelectDirectory = 'UCAP::file::selectDirectory' SelectDirectory = 'UCAP::file::selectDirectory',
SelectSaveFilePath = 'UCAP::file::SelectSaveFilePath'
} }
export enum WindowStateChannel { export enum WindowStateChannel {

View File

@ -58,6 +58,7 @@ export abstract class NativeService {
abstract openTargetItem(filePath?: string): Promise<boolean>; abstract openTargetItem(filePath?: string): Promise<boolean>;
abstract getPath(name: NativePathName): Promise<string>; abstract getPath(name: NativePathName): Promise<string>;
abstract selectDirectory(): Promise<string>; abstract selectDirectory(): Promise<string>;
abstract selectSaveFilePath(defaultPath?: string): Promise<string>;
abstract windowStateChanged(): Observable<WindowState>; abstract windowStateChanged(): Observable<WindowState>;
abstract windowClose(): void; abstract windowClose(): void;

View File

@ -15,6 +15,7 @@ import { RoleCode } from '@ucap-webmessenger/protocol-authentication';
import { DeptSearchType } from '../types/dept-search.type'; import { DeptSearchType } from '../types/dept-search.type';
import { CallMode } from '@ucap-webmessenger/core'; import { CallMode } from '@ucap-webmessenger/core';
import { UserInfoSS } from '../models/user-info-ss'; import { UserInfoSS } from '../models/user-info-ss';
import { WorkStatusType } from 'projects/ucap-webmessenger-protocol-status/src/lib/types/work-status.type';
export interface DeptUserRequest extends ProtocolRequest { export interface DeptUserRequest extends ProtocolRequest {
/** DivCD(s) */ /** DivCD(s) */
@ -113,7 +114,15 @@ export const decodeDeptUserData: ProtocolDecoder<DeptUserData> = (
deptSeq: info[25], deptSeq: info[25],
isPrivacyAgree: info[26] === 'Y' ? true : false, isPrivacyAgree: info[26] === 'Y' ? true : false,
isValidLogin: info[27] === 'Y' ? true : false, isValidLogin: info[27] === 'Y' ? true : false,
employeeType: info[28] as EmployeeType employeeType: info[28] as EmployeeType,
// [daesang]
companyName: info[29],
responsibilities: info[30],
workstatus: info[31] as WorkStatusType,
job: info[32],
customerInfo: info[33],
workplace: info[34]
}); });
}); });
return decodeProtocolMessage(message, { return decodeProtocolMessage(message, {

View File

@ -1,4 +1,4 @@
<div class="bubble-main"> <div class="bubble-main" (click)="onClickOpenViewer()">
<!--파일명에 따라 doc exe hwp ppt xls zip 으로 추가되고 나머지 파일 명은 file로 기간이 만료된 파일은 그뒤에 disable도 추가--> <!--파일명에 따라 doc exe hwp ppt xls zip 으로 추가되고 나머지 파일 명은 file로 기간이 만료된 파일은 그뒤에 disable도 추가-->
<!-- <div class="file-img" [ngClass]="fileInfo.FileExt"></div> --> <!-- <div class="file-img" [ngClass]="fileInfo.FileExt"></div> -->
<div [ngClass]="['mime-icon', 'light', 'ico-' + fileInfo.fileExt]"> <div [ngClass]="['mime-icon', 'light', 'ico-' + fileInfo.fileExt]">

View File

@ -15,6 +15,8 @@ export class AttachFileComponent implements OnInit {
@Output() @Output()
save = new EventEmitter<string>(); save = new EventEmitter<string>();
@Output()
openViewer = new EventEmitter();
constructor(private logger: NGXLogger) {} constructor(private logger: NGXLogger) {}
@ -26,4 +28,7 @@ export class AttachFileComponent implements OnInit {
onClickSaveAs() { onClickSaveAs() {
this.save.emit('saveAs'); this.save.emit('saveAs');
} }
onClickOpenViewer() {
this.openViewer.emit();
}
} }

View File

@ -6,7 +6,7 @@
*ngSwitchCase="FileType.File" *ngSwitchCase="FileType.File"
[fileInfo]="fileInfo" [fileInfo]="fileInfo"
[expired]="getExpiredFile()" [expired]="getExpiredFile()"
(click)="onClickFileViewer(fileInfo)" (openViewer)="onClickFileViewer(fileInfo)"
(save)="onSave($event)" (save)="onSave($event)"
> >
</ucap-chat-message-box-attach-file> </ucap-chat-message-box-attach-file>
@ -14,7 +14,7 @@
*ngSwitchCase="FileType.Sound" *ngSwitchCase="FileType.Sound"
[fileInfo]="fileInfo" [fileInfo]="fileInfo"
[expired]="getExpiredFile()" [expired]="getExpiredFile()"
(click)="onClickFileViewer(fileInfo)" (openViewer)="onClickFileViewer(fileInfo)"
(save)="onSave($event)" (save)="onSave($event)"
> >
</ucap-chat-message-box-attach-file> </ucap-chat-message-box-attach-file>
@ -28,7 +28,8 @@
*ngSwitchCase="FileType.Video" *ngSwitchCase="FileType.Video"
[fileInfo]="fileInfo" [fileInfo]="fileInfo"
[expired]="getExpiredFile()" [expired]="getExpiredFile()"
(click)="onClickFileViewer(fileInfo)" (openViewer)="onClickFileViewer(fileInfo)"
(save)="onSave($event)"
></ucap-chat-message-box-video> ></ucap-chat-message-box-video>
<ucap-chat-message-box-text <ucap-chat-message-box-text
*ngSwitchDefault *ngSwitchDefault

View File

@ -4,7 +4,7 @@ import {
InfoResponse, InfoResponse,
FileEventJson FileEventJson
} from '@ucap-webmessenger/protocol-event'; } from '@ucap-webmessenger/protocol-event';
import { StatusCode } from '@ucap-webmessenger/api'; import { StatusCode, FileDownloadItem } from '@ucap-webmessenger/api';
import { FileType } from '@ucap-webmessenger/protocol-file'; import { FileType } from '@ucap-webmessenger/protocol-file';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
@ -20,11 +20,16 @@ export class FileComponent implements OnInit {
eventInfoStatus: InfoResponse; eventInfoStatus: InfoResponse;
@Output() @Output()
save = new EventEmitter<{ fileInfo: FileEventJson; type: string }>(); save = new EventEmitter<{
fileInfo: FileEventJson;
fileDownloadItem: FileDownloadItem;
type: string;
}>();
@Output() @Output()
fileViewer = new EventEmitter<FileEventJson>(); fileViewer = new EventEmitter<FileEventJson>();
fileInfo?: FileEventJson; fileInfo?: FileEventJson;
fileDownloadItem: FileDownloadItem;
errorMessage?: string; errorMessage?: string;
FileType = FileType; FileType = FileType;
@ -37,6 +42,8 @@ export class FileComponent implements OnInit {
this.errorMessage = this.errorMessage =
this.message.sentMessageJson.errorMessage || '[Error] System Error!!'; this.message.sentMessageJson.errorMessage || '[Error] System Error!!';
} }
this.fileDownloadItem = new FileDownloadItem();
} }
getExpiredFile() { getExpiredFile() {
@ -58,7 +65,11 @@ export class FileComponent implements OnInit {
onSave(value: string) { onSave(value: string) {
if (!this.getExpiredFile()) { if (!this.getExpiredFile()) {
this.save.emit({ fileInfo: this.fileInfo, type: value }); this.save.emit({
fileInfo: this.fileInfo,
fileDownloadItem: this.fileDownloadItem,
type: value
});
} }
} }
} }

View File

@ -1,4 +1,4 @@
<div class="bubble-main"> <div class="bubble-main" (click)="onClickOpenViewer()">
<div class="file-thumbimg"> <div class="file-thumbimg">
<img *ngIf="!!fileInfo.thumbUrl" [src]="fileInfo.thumbUrl" /> <img *ngIf="!!fileInfo.thumbUrl" [src]="fileInfo.thumbUrl" />
</div> </div>

View File

@ -15,6 +15,8 @@ export class VideoComponent implements OnInit {
@Output() @Output()
save = new EventEmitter<string>(); save = new EventEmitter<string>();
@Output()
openViewer = new EventEmitter();
constructor(private logger: NGXLogger) {} constructor(private logger: NGXLogger) {}
@ -26,4 +28,7 @@ export class VideoComponent implements OnInit {
onClickSaveAs() { onClickSaveAs() {
this.save.emit('saveAs'); this.save.emit('saveAs');
} }
onClickOpenViewer() {
this.openViewer.emit();
}
} }

View File

@ -14,6 +14,7 @@ import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
import { FileInfo } from '../models/file-info.json'; import { FileInfo } from '../models/file-info.json';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import moment from 'moment'; import moment from 'moment';
import { FileDownloadItem } from '@ucap-webmessenger/api';
@Component({ @Component({
selector: 'ucap-chat-messages', selector: 'ucap-chat-messages',
@ -67,7 +68,11 @@ export class MessagesComponent implements OnInit {
@Output() @Output()
fileViewer = new EventEmitter<FileEventJson>(); fileViewer = new EventEmitter<FileEventJson>();
@Output() @Output()
save = new EventEmitter<{ fileInfo: FileInfo; type: string }>(); save = new EventEmitter<{
fileInfo: FileEventJson;
fileDownloadItem: FileDownloadItem;
type: string;
}>();
@Output() @Output()
contextMenu = new EventEmitter<{ contextMenu = new EventEmitter<{
event: MouseEvent; event: MouseEvent;
@ -233,7 +238,11 @@ export class MessagesComponent implements OnInit {
} }
/** [Event] Attach File Save & Save As */ /** [Event] Attach File Save & Save As */
onSave(value: { fileInfo: FileInfo; type: string }) { onSave(value: {
fileInfo: FileEventJson;
fileDownloadItem: FileDownloadItem;
type: string;
}) {
this.save.emit(value); this.save.emit(value);
} }