This commit is contained in:
병준 박 2019-12-04 17:59:10 +09:00
commit aac8802822
19 changed files with 255 additions and 39 deletions

View File

@ -26,7 +26,8 @@ export enum ElectronBrowserWindowChannel {
Show = 'show', Show = 'show',
Close = 'close', Close = 'close',
Closed = 'closed', Closed = 'closed',
ReadyToShow = 'ready-to-show' ReadyToShow = 'ready-to-show',
Focus = 'focus'
} }
export enum ElectronWebContentsChannel { export enum ElectronWebContentsChannel {

View File

@ -65,7 +65,7 @@ export class AppWindow {
this.window = new BrowserWindow(windowOptions); this.window = new BrowserWindow(windowOptions);
savedWindowState.manage(this.window); savedWindowState.manage(this.window);
let quitting = false; let quitting = true;
app.on(ElectronAppChannel.BeforeQuit, () => { app.on(ElectronAppChannel.BeforeQuit, () => {
quitting = true; quitting = true;
}); });
@ -92,10 +92,13 @@ export class AppWindow {
} }
}); });
this.window.on(ElectronBrowserWindowChannel.Close, e => { this.window.on(ElectronBrowserWindowChannel.Close, e => {
if (!quitting) { // if (!quitting) {
e.preventDefault(); e.preventDefault();
this.window.hide(); this.window.hide();
} // }
});
this.window.on(ElectronBrowserWindowChannel.Focus, e => {
this.window.flashFrame(false);
}); });
} }

View File

@ -5,7 +5,8 @@ import {
remote, remote,
Tray, Tray,
Menu, Menu,
dialog dialog,
shell
} from 'electron'; } from 'electron';
import * as path from 'path'; import * as path from 'path';
import * as url from 'url'; import * as url from 'url';
@ -319,6 +320,38 @@ ipcMain.on(
} }
); );
ipcMain.on(
FileChannel.OpenFolderItem,
async (event: IpcMainEvent, ...args: any[]) => {
try {
let folderItem: string = args[0];
const make: boolean = args[1];
if (!folderItem) {
folderItem = DefaultFolder.downloads();
}
let isSuccess = true;
if (make) {
fse.ensureDirSync(folderItem);
}
if (isSuccess && fse.existsSync(folderItem)) {
shell.openItem(folderItem);
} else {
isSuccess = false;
}
if (isSuccess) {
event.returnValue = true;
} else {
event.returnValue = false;
}
} catch (error) {
event.returnValue = false;
}
}
);
ipcMain.on( ipcMain.on(
IdleStateChannel.StartCheck, IdleStateChannel.StartCheck,
(event: IpcMainEvent, ...args: any[]) => { (event: IpcMainEvent, ...args: any[]) => {
@ -350,6 +383,7 @@ ipcMain.on(
) )
: '', : '',
onClick: e => { onClick: e => {
appWindow.browserWindow.flashFrame(false);
appWindow.browserWindow.webContents.send( appWindow.browserWindow.webContents.send(
ChatChannel.OpenRoom, ChatChannel.OpenRoom,
noti.roomSeq noti.roomSeq
@ -358,12 +392,15 @@ ipcMain.on(
e.close(); e.close();
} }
}); });
appWindow.browserWindow.flashFrame(true);
} }
); );
ipcMain.on( ipcMain.on(
NotificationChannel.CloseAllNotify, NotificationChannel.CloseAllNotify,
(event: IpcMainEvent, ...args: any[]) => { (event: IpcMainEvent, ...args: any[]) => {
appWindow.browserWindow.flashFrame(false);
console.log('Channel.closeAllNotify', args); console.log('Channel.closeAllNotify', args);
} }
); );

View File

@ -172,7 +172,7 @@
> >
<ucap-chat-messages <ucap-chat-messages
[messages]="eventList" [messages]="eventList"
[eventInfoStatus]="eventInfoStatus$ | async" [eventInfoStatus]="eventInfoStatus"
[eventRemain]="eventRemain$ | async" [eventRemain]="eventRemain$ | async"
[userInfos]="userInfoList" [userInfos]="userInfoList"
[loginRes]="loginRes" [loginRes]="loginRes"
@ -248,12 +248,14 @@
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="isForwardableMessage(message, eventInfoStatus.validFileBaseSeq)"
(click)="onClickMessageContextMenu('REPLAY', message)" (click)="onClickMessageContextMenu('REPLAY', message)"
> >
대화 전달 대화 전달
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="isForwardableMessage(message, eventInfoStatus.validFileBaseSeq)"
(click)="onClickMessageContextMenu('REPLAY_TO_ME', message)" (click)="onClickMessageContextMenu('REPLAY_TO_ME', message)"
> >
대화 나에게 전달 대화 나에게 전달

View File

@ -31,6 +31,7 @@ import {
isRecalled, isRecalled,
isCopyable, isCopyable,
isRecallable, isRecallable,
isForwardable,
InfoResponse, InfoResponse,
EventJson, EventJson,
FileEventJson, FileEventJson,
@ -144,7 +145,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
userInfoList: UserInfo[]; userInfoList: UserInfo[];
userInfoListSubscription: Subscription; userInfoListSubscription: Subscription;
eventListProcessing$: Observable<boolean>; eventListProcessing$: Observable<boolean>;
eventInfoStatus$: Observable<InfoResponse>; eventInfoStatus: InfoResponse;
eventInfoStatusSubscription: Subscription;
sessionVerInfo: VersionInfo2Response; sessionVerInfo: VersionInfo2Response;
eventRemain$: Observable<boolean>; eventRemain$: Observable<boolean>;
@ -155,6 +157,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
isRecalledMessage = isRecalled; isRecalledMessage = isRecalled;
isCopyableMessage = isCopyable; isCopyableMessage = isCopyable;
isRecallableMessage = isRecallable; isRecallableMessage = isRecallable;
isForwardableMessage = isForwardable;
/** Timer 대화방의 대화 삭제를 위한 interval */ /** Timer 대화방의 대화 삭제를 위한 interval */
interval: any; interval: any;
@ -253,9 +256,12 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
) )
.subscribe(); .subscribe();
this.eventInfoStatus$ = this.store.pipe( this.eventInfoStatusSubscription = this.store
select(AppStore.MessengerSelector.EventSelector.infoStatus) .pipe(
); select(AppStore.MessengerSelector.EventSelector.infoStatus),
tap(res => (this.eventInfoStatus = res))
)
.subscribe();
this.interval = setInterval(() => { this.interval = setInterval(() => {
if (!!this.roomInfo && !!this.roomInfo.isTimeRoom) { if (!!this.roomInfo && !!this.roomInfo.isTimeRoom) {
@ -277,6 +283,9 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
if (!!this.eventListSubscription) { if (!!this.eventListSubscription) {
this.eventListSubscription.unsubscribe(); this.eventListSubscription.unsubscribe();
} }
if (!!this.eventInfoStatusSubscription) {
this.eventInfoStatusSubscription.unsubscribe();
}
clearInterval(this.interval); clearInterval(this.interval);
} }

View File

@ -149,7 +149,11 @@
> >
Download All Download All
</button> </button>
<button mat-flat-button class="mat-primary"> <button
mat-flat-button
class="mat-primary"
(click)="onClickOpenDownloadFolder()"
>
Open Folder Open Folder
</button> </button>
</div> </div>

View File

@ -1,9 +1,9 @@
import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; import { Component, OnInit, ViewChild, OnDestroy, Inject } from '@angular/core';
import { MatPaginator, MatTableDataSource } from '@angular/material'; import { MatPaginator, MatTableDataSource } from '@angular/material';
import { import {
FileInfo, FileInfo,
FileDownloadInfo, FileDownloadInfo,
FileType, FileType
} from '@ucap-webmessenger/protocol-file'; } from '@ucap-webmessenger/protocol-file';
import { Subscription, combineLatest } from 'rxjs'; import { Subscription, combineLatest } from 'rxjs';
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
@ -14,7 +14,7 @@ import { tap, map } from 'rxjs/operators';
import { import {
Info, Info,
EventJson, EventJson,
FileEventJson, FileEventJson
} from '@ucap-webmessenger/protocol-event'; } from '@ucap-webmessenger/protocol-event';
import { FileUtil } from '@ucap-webmessenger/core'; import { FileUtil } from '@ucap-webmessenger/core';
import { CommonApiService } from '@ucap-webmessenger/api-common'; import { CommonApiService } from '@ucap-webmessenger/api-common';
@ -24,6 +24,8 @@ import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types'; import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
import { KEY_VER_INFO } from '@app/types/ver-info.type'; import { KEY_VER_INFO } from '@app/types/ver-info.type';
import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native';
import { NGXLogger } from 'ngx-logger';
export interface FileInfoTotal { export interface FileInfoTotal {
info: FileInfo; info: FileInfo;
@ -34,7 +36,7 @@ export interface FileInfoTotal {
@Component({ @Component({
selector: 'app-layout-chat-right-drawer-album-box', selector: 'app-layout-chat-right-drawer-album-box',
templateUrl: './album-box.component.html', templateUrl: './album-box.component.html',
styleUrls: ['./album-box.component.scss'], styleUrls: ['./album-box.component.scss']
}) })
export class AlbumBoxComponent implements OnInit, OnDestroy { export class AlbumBoxComponent implements OnInit, OnDestroy {
filteredList: FileInfoTotal[] = []; filteredList: FileInfoTotal[] = [];
@ -55,7 +57,9 @@ export class AlbumBoxComponent implements OnInit, OnDestroy {
constructor( constructor(
private store: Store<any>, private store: Store<any>,
private sessionStorageService: SessionStorageService, private sessionStorageService: SessionStorageService,
private commonApiService: CommonApiService private commonApiService: CommonApiService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private loggger: NGXLogger
) { ) {
this.loginRes = this.sessionStorageService.get<LoginResponse>( this.loginRes = this.sessionStorageService.get<LoginResponse>(
KEY_LOGIN_RES_INFO KEY_LOGIN_RES_INFO
@ -81,7 +85,7 @@ export class AlbumBoxComponent implements OnInit, OnDestroy {
), ),
this.store.pipe( this.store.pipe(
select(AppStore.MessengerSelector.EventSelector.selectAllInfoList) select(AppStore.MessengerSelector.EventSelector.selectAllInfoList)
), )
]) ])
.pipe( .pipe(
tap(() => (this.fileInfoTotal = [])), tap(() => (this.fileInfoTotal = [])),
@ -109,7 +113,7 @@ export class AlbumBoxComponent implements OnInit, OnDestroy {
checkInfo => checkInfo.seq === fileInfo.seq checkInfo => checkInfo.seq === fileInfo.seq
), ),
eventInfo: eventInfo:
events.length > 0 ? (events[0] as Info<FileEventJson>) : null, events.length > 0 ? (events[0] as Info<FileEventJson>) : null
}); });
}); });
@ -135,7 +139,7 @@ export class AlbumBoxComponent implements OnInit, OnDestroy {
userSeq: this.loginRes.userSeq, userSeq: this.loginRes.userSeq,
deviceType: this.environmentsInfo.deviceType, deviceType: this.environmentsInfo.deviceType,
token: this.loginRes.tokenString, token: this.loginRes.tokenString,
attachmentsSeq: fileInfo.info.seq, attachmentsSeq: fileInfo.info.seq
}, },
this.sessionVerinfo.downloadUrl this.sessionVerinfo.downloadUrl
); );
@ -195,4 +199,18 @@ export class AlbumBoxComponent implements OnInit, OnDestroy {
onClickDownload(fileInfo: FileInfoTotal) { onClickDownload(fileInfo: FileInfoTotal) {
console.log(fileInfo); console.log(fileInfo);
} }
onClickOpenDownloadFolder(): void {
this.nativeService
.openDefaultDownloadFolder()
.then(result => {
if (!!result) {
} else {
throw new Error('response Error');
}
})
.catch(reason => {
this.loggger.error(reason);
});
}
} }

View File

@ -202,7 +202,11 @@
> >
Download All Download All
</button> </button>
<button mat-flat-button class="mat-primary"> <button
mat-flat-button
class="mat-primary"
(click)="onClickOpenDownloadFolder()"
>
Open Folder Open Folder
</button> </button>
</div> </div>

View File

@ -1,4 +1,4 @@
import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; import { Component, OnInit, ViewChild, OnDestroy, Inject } from '@angular/core';
import { MatPaginator, MatTableDataSource, MatSort } from '@angular/material'; import { MatPaginator, MatTableDataSource, MatSort } from '@angular/material';
import { import {
FileInfo, FileInfo,
@ -15,6 +15,8 @@ import { FileUtil } from '@ucap-webmessenger/core';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type'; 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';
export interface FileInfoTotal { export interface FileInfoTotal {
info: FileInfo; info: FileInfo;
@ -46,7 +48,9 @@ export class FileBoxComponent implements OnInit, OnDestroy {
constructor( constructor(
private store: Store<any>, private store: Store<any>,
private sessionStorageService: SessionStorageService private sessionStorageService: SessionStorageService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private loggger: NGXLogger
) { ) {
this.loginRes = this.sessionStorageService.get<LoginResponse>( this.loginRes = this.sessionStorageService.get<LoginResponse>(
KEY_LOGIN_RES_INFO KEY_LOGIN_RES_INFO
@ -212,4 +216,18 @@ export class FileBoxComponent implements OnInit, OnDestroy {
onClickRow(row: FileInfoTotal) { onClickRow(row: FileInfoTotal) {
this.selectedFile = row; this.selectedFile = row;
} }
onClickOpenDownloadFolder(): void {
this.nativeService
.openDefaultDownloadFolder()
.then(result => {
if (!!result) {
} else {
throw new Error('response Error');
}
})
.catch(reason => {
this.loggger.error(reason);
});
}
} }

View File

@ -99,6 +99,22 @@ export class BrowserNativeService extends NativeService {
}); });
} }
openDefaultDownloadFolder(): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
resolve(true);
});
}
openTargetFolder(folderPath?: string, make?: boolean): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
resolve(true);
});
}
openTargetItem(filePath?: string): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
resolve(true);
});
}
windowStateChanged(): Observable<WindowState> { windowStateChanged(): Observable<WindowState> {
return new Observable<WindowState>(subscriber => { return new Observable<WindowState>(subscriber => {
try { try {

View File

@ -147,6 +147,44 @@ export class ElectronNativeService implements NativeService {
}); });
} }
openDefaultDownloadFolder(): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
try {
resolve(
this.ipcRenderer.sendSync(FileChannel.OpenFolderItem, undefined, true)
);
} catch (error) {
reject(error);
}
});
}
openTargetFolder(folderPath?: string, make?: boolean): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
try {
resolve(
this.ipcRenderer.sendSync(
FileChannel.OpenFolderItem,
folderPath,
!make ? false : make
)
);
} catch (error) {
reject(error);
}
});
}
openTargetItem(filePath?: string): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
try {
resolve(
this.ipcRenderer.sendSync(FileChannel.OpenFolderItem, filePath, false)
);
} 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

@ -18,6 +18,7 @@ export enum UpdaterChannel {
} }
export enum FileChannel { export enum FileChannel {
OpenFolderItem = 'UCAP::file::openFolderItem',
ShowImageViewer = 'UCAP::file::showImageViewer', ShowImageViewer = 'UCAP::file::showImageViewer',
SaveFile = 'UCAP::file::saveFile', SaveFile = 'UCAP::file::saveFile',
ReadFile = 'UCAP::file::readFile' ReadFile = 'UCAP::file::readFile'

View File

@ -27,6 +27,12 @@ export abstract class NativeService {
path?: string path?: string
): Promise<string>; ): Promise<string>;
abstract readFile(path: string): Promise<Buffer>; abstract readFile(path: string): Promise<Buffer>;
abstract openDefaultDownloadFolder(): Promise<boolean>;
abstract openTargetFolder(
folderPath?: string,
make?: boolean
): Promise<boolean>;
abstract openTargetItem(filePath?: string): Promise<boolean>;
abstract windowStateChanged(): Observable<WindowState>; abstract windowStateChanged(): Observable<WindowState>;
abstract windowClose(): void; abstract windowClose(): void;

View File

@ -32,3 +32,16 @@ export function isRecallable(event: Info<any>, userSeq: number): boolean {
event.senderSeq === userSeq && event.type !== EventType.RecalledMessage event.senderSeq === userSeq && event.type !== EventType.RecalledMessage
); );
} }
export function isForwardable(event: Info, expiredFileStdSeq: number): boolean {
if (event.type === EventType.File) {
if (!!expiredFileStdSeq && expiredFileStdSeq <= event.seq) {
// valid..
return true;
} else {
// expired..
return false;
}
}
return true;
}

View File

@ -2,6 +2,7 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
padding: 14px; padding: 14px;
min-width: 200px;
.file-img { .file-img {
display: inline-flex; display: inline-flex;
width: 50px; width: 50px;
@ -102,13 +103,13 @@
height: 100%; height: 100%;
} }
} }
&.expired{ &.expired {
li{ li {
width:100%; width: 100%;
white-space: nowrap; white-space: nowrap;
color:#999999; color: #999999;
align-items: center; align-items: center;
line-height:40px; line-height: 40px;
} }
} }
} }

View File

@ -51,10 +51,14 @@ export class FileComponent implements OnInit {
} }
onClickFileViewer(fileInfo: FileEventJson) { onClickFileViewer(fileInfo: FileEventJson) {
if (!this.getExpiredFile()) {
this.fileViewer.emit(fileInfo); this.fileViewer.emit(fileInfo);
} }
}
onSave(value: string) { onSave(value: string) {
if (!this.getExpiredFile()) {
this.save.emit({ fileInfo: this.fileInfo, type: value }); this.save.emit({ fileInfo: this.fileInfo, type: value });
} }
}
} }

View File

@ -1,3 +1,10 @@
<div class="bubble-main"> <div
class="bubble-main"
(mouseenter)="mouseEnter($event)"
(mouseleave)="mouseLeave($event)"
>
<div *ngIf="showExpired" class="expired-text">
<span>기간이 만료된 파일입니다</span>
</div>
<img [src]="fileInfo.thumbUrl" /> <img [src]="fileInfo.thumbUrl" />
</div> </div>

View File

@ -1,7 +1,24 @@
.bubble-main{ .bubble-main {
padding:10px; padding: 10px;
img{ position: relative;
height:140px;
width:auto; img {
height: 140px;
width: auto;
}
.expired-text {
position: absolute;
background-color: rgb(255, 255, 255, 0.6);
width: calc(100% - 20px);
height: calc(100% - 20px);
text-align: center;
display: table;
span {
color: #666666;
display: table-cell;
vertical-align: middle;
}
} }
} }

View File

@ -1,4 +1,4 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
import { FileEventJson } from '@ucap-webmessenger/protocol-event'; import { FileEventJson } from '@ucap-webmessenger/protocol-event';
@ -13,7 +13,24 @@ export class ImageComponent implements OnInit {
@Input() @Input()
expired = false; expired = false;
showExpired = false;
constructor(private logger: NGXLogger) {} constructor(private logger: NGXLogger) {}
ngOnInit() {} ngOnInit() {}
mouseEnter(event: MouseEvent): void {
if (this.expired) {
this.showExpired = true;
}
event.stopPropagation();
event.preventDefault();
}
mouseLeave(event: MouseEvent): void {
if (this.expired) {
this.showExpired = false;
}
event.stopPropagation();
event.preventDefault();
}
} }