import { ipcRenderer, remote, shell } from 'electron'; import { Observable, Subject } from 'rxjs'; import { NativeService, WindowState, NotificationRequest, WindowIdle, UpdateInfo, UpdateCheckConfig } from '@ucap-webmessenger/native'; import { share } from 'rxjs/operators'; import { NotificationChannel, UpdaterChannel, FileChannel, WindowStateChannel, IdleStateChannel, ChatChannel, MessengerChannel, MessageChannel } from '../types/channel.type'; import { Injectable } from '@angular/core'; import { TranslateLoaderService } from '../translate/electron-loader'; import { TranslateLoader } from '@ngx-translate/core'; import { StatusCode } from '@ucap-webmessenger/core'; @Injectable({ providedIn: 'root' }) export class ElectronNativeService implements NativeService { private ipcRenderer: typeof ipcRenderer; private remote: typeof remote; private shell: typeof shell; private logoutSubject: Subject | null = null; private logout$: Observable | null = null; private changeStatusSubject: Subject | null = null; private changeStatus$: Observable | null = null; private showSettingSubject: Subject | null = null; private showSetting$: Observable | null = null; private windowStateChangedSubject: Subject | null = null; private windowStateChanged$: Observable | null = null; private idleStateChangedSubject: Subject | null = null; private idleStateChanged$: Observable | null = null; private chatOpenRoomSubject: Subject | null = null; private chatOpenRoom$: Observable | null = null; private msgOpenMessageSubject: Subject | null = null; private msgOpenMessage$: Observable | null = null; private backgroundCheckForUpdatesSubject: Subject | null = null; private backgroundCheckForUpdates$: Observable | null = null; postAppInit(): void {} logout(): Observable { if (!this.logoutSubject) { this.logoutSubject = new Subject(); this.logout$ = this.logoutSubject.asObservable().pipe(share()); } this.ipcRenderer.on(MessengerChannel.Logout, (event: any) => { this.logoutSubject.next(); }); return this.logout$; } changeStatus(): Observable { if (!this.changeStatusSubject) { this.changeStatusSubject = new Subject(); this.changeStatus$ = this.changeStatusSubject .asObservable() .pipe(share()); } this.ipcRenderer.on( MessengerChannel.ChangeStatus, (event: any, statusCode: StatusCode) => { this.changeStatusSubject.next(statusCode); } ); return this.changeStatus$; } showSetting(): Observable { if (!this.showSettingSubject) { this.showSettingSubject = new Subject(); this.showSetting$ = this.showSettingSubject.asObservable().pipe(share()); } this.ipcRenderer.on(MessengerChannel.ShowSetting, (event: any) => { this.showSettingSubject.next(); }); return this.showSetting$; } changeAutoLaunch(autoLaunch: boolean): Promise { return new Promise((resolve, reject) => { try { resolve( this.ipcRenderer.sendSync( MessengerChannel.ChangeAutoLaunch, autoLaunch ) ); } catch (error) { reject(error); } }); } notify(noti: NotificationRequest): void { this.ipcRenderer.send(NotificationChannel.Notify, noti); } closeAllNotify(): void { this.ipcRenderer.send(NotificationChannel.CloseAllNotify); } checkForUpdates(currentVersion: string): Promise { return new Promise((resolve, reject) => { try { resolve( this.ipcRenderer.sendSync(UpdaterChannel.Check, currentVersion) ); } catch (error) { reject(error); } }); } checkForInstantUpdates(config: UpdateCheckConfig): Observable { if (!this.backgroundCheckForUpdatesSubject) { this.backgroundCheckForUpdatesSubject = new Subject(); this.backgroundCheckForUpdates$ = this.backgroundCheckForUpdatesSubject .asObservable() .pipe(share()); } this.ipcRenderer.send(UpdaterChannel.StartCheckInstant, config); this.ipcRenderer.on( UpdaterChannel.ExistInstant, (event: any, updateInfo: UpdateInfo) => { this.backgroundCheckForUpdatesSubject.next(updateInfo); } ); return this.backgroundCheckForUpdates$; } applyInstantUpdates(): void { this.ipcRenderer.send(UpdaterChannel.ApplyInstant); } showImageViewer(): void { this.ipcRenderer.send(FileChannel.ShowImageViewer); } readFile(path: string): Promise { return new Promise((resolve, reject) => { try { resolve(this.ipcRenderer.sendSync(FileChannel.ReadFile, path)); } catch (error) { reject(error); } }); } saveFile( buffer: Buffer, fileName: string, mimeType: string, path?: string ): Promise { return new Promise((resolve, reject) => { try { resolve( this.ipcRenderer.sendSync( FileChannel.SaveFile, buffer, fileName, mimeType, path ) ); } catch (error) { reject(error); } }); } openDefaultDownloadFolder(): Promise { return new Promise((resolve, reject) => { try { resolve( this.ipcRenderer.sendSync(FileChannel.OpenFolderItem, undefined, true) ); } catch (error) { reject(error); } }); } openTargetFolder(folderPath?: string, make?: boolean): Promise { return new Promise((resolve, reject) => { try { resolve( this.ipcRenderer.sendSync( FileChannel.OpenFolderItem, folderPath, !make ? false : make ) ); } catch (error) { reject(error); } }); } openTargetItem(filePath?: string): Promise { return new Promise((resolve, reject) => { try { resolve( this.ipcRenderer.sendSync(FileChannel.OpenFolderItem, filePath, false) ); } catch (error) { reject(error); } }); } windowStateChanged(): Observable { if (!this.windowStateChangedSubject) { this.windowStateChangedSubject = new Subject(); this.windowStateChanged$ = this.windowStateChangedSubject .asObservable() .pipe(share()); } this.ipcRenderer.on( WindowStateChannel.Changed, (event: any, windowState: WindowState) => { this.windowStateChangedSubject.next(windowState); } ); return this.windowStateChanged$; } windowClose(): void { const currentWindow = this.remote.getCurrentWindow(); if (!currentWindow) { return; } currentWindow.close(); } windowMinimize(): void { const currentWindow = this.remote.getCurrentWindow(); if (!currentWindow) { return; } currentWindow.minimize(); } windowMaximize(): void { const currentWindow = this.remote.getCurrentWindow(); if (!currentWindow) { return; } if (currentWindow.isMaximized()) { currentWindow.unmaximize(); } else { currentWindow.maximize(); } } idleStateChanged(): Observable { if (!this.idleStateChangedSubject) { this.idleStateChangedSubject = new Subject(); this.idleStateChanged$ = this.idleStateChangedSubject .asObservable() .pipe(share()); } this.ipcRenderer.send(IdleStateChannel.StartCheck); this.ipcRenderer.on( IdleStateChannel.Changed, (event: any, idleState: WindowIdle) => { this.idleStateChangedSubject.next(idleState); } ); return this.idleStateChanged$; } chatOpenRoom(): Observable { if (!this.chatOpenRoomSubject) { this.chatOpenRoomSubject = new Subject(); this.chatOpenRoom$ = this.chatOpenRoomSubject .asObservable() .pipe(share()); } this.ipcRenderer.on(ChatChannel.OpenRoom, (event: any, roomSeq: string) => { this.chatOpenRoomSubject.next(roomSeq); }); return this.chatOpenRoom$; } msgOpenMessage(): Observable { if (!this.msgOpenMessageSubject) { this.msgOpenMessageSubject = new Subject(); this.msgOpenMessage$ = this.msgOpenMessageSubject .asObservable() .pipe(share()); } this.ipcRenderer.on( MessageChannel.OpenMessage, (event: any, messageSeq: string) => { this.msgOpenMessageSubject.next(messageSeq); } ); return this.msgOpenMessage$; } getTranslateLoader(prefix?: string, suffix?: string): TranslateLoader { return new TranslateLoaderService(this, prefix, suffix); } openDefaultBrowser(url: string): void { this.shell.openExternal(url); } get isElectron() { return window && (window as any).process && (window as any).process.type; } constructor() { if (this.isElectron) { this.ipcRenderer = (window as any).require('electron').ipcRenderer; this.remote = (window as any).require('electron').remote; this.shell = (window as any).require('electron').shell; } } }