2019-11-18 06:02:24 +00:00
|
|
|
import {
|
|
|
|
app,
|
|
|
|
ipcMain,
|
|
|
|
IpcMainEvent,
|
|
|
|
remote,
|
|
|
|
Tray,
|
|
|
|
Menu,
|
|
|
|
dialog
|
|
|
|
} from 'electron';
|
2019-11-11 06:53:39 +00:00
|
|
|
import * as path from 'path';
|
|
|
|
import * as url from 'url';
|
|
|
|
import * as fse from 'fs-extra';
|
|
|
|
import * as fs from 'fs';
|
|
|
|
|
|
|
|
import { AppWindow } from './app/AppWindow';
|
|
|
|
import { now } from './util/now';
|
|
|
|
import { showUncaughtException } from './crash/show-uncaught-exception';
|
|
|
|
|
|
|
|
import {
|
|
|
|
UpdaterChannel,
|
|
|
|
FileChannel,
|
|
|
|
IdleStateChannel,
|
2019-11-12 01:38:51 +00:00
|
|
|
NotificationChannel,
|
2019-11-19 07:44:50 +00:00
|
|
|
ChatChannel,
|
|
|
|
MessengerChannel
|
2019-11-11 06:53:39 +00:00
|
|
|
} from '@ucap-webmessenger/native-electron';
|
|
|
|
import { ElectronNotificationService } from '@ucap-webmessenger/electron-notification';
|
|
|
|
|
|
|
|
import { root } from './util/root';
|
|
|
|
import { DefaultFolder } from './lib/default-folder';
|
|
|
|
import { FileUtil } from './lib/file-util';
|
|
|
|
|
|
|
|
import { IdleChecker } from './lib/idle-checker';
|
|
|
|
import { NotificationRequest } from '@ucap-webmessenger/native';
|
|
|
|
import { ElectronAppChannel } from '@ucap-webmessenger/electron-core';
|
|
|
|
|
2019-11-18 06:02:24 +00:00
|
|
|
const appIconPath = __LINUX__
|
|
|
|
? path.join(__dirname, 'static', 'icon-logo.png')
|
|
|
|
: path.join(__dirname, 'resources/image', 'ico_64_64.png');
|
|
|
|
|
2019-11-11 06:53:39 +00:00
|
|
|
let appWindow: AppWindow | null = null;
|
2019-11-18 06:02:24 +00:00
|
|
|
let appTray: Tray | null = null;
|
2019-11-11 06:53:39 +00:00
|
|
|
|
|
|
|
const launchTime = now();
|
|
|
|
let readyTime: number | null = null;
|
|
|
|
|
|
|
|
type OnDidLoadFn = (window: AppWindow) => void;
|
|
|
|
let onDidLoadFns: Array<OnDidLoadFn> | null = [];
|
|
|
|
|
|
|
|
let preventQuit = false;
|
|
|
|
|
|
|
|
let notificationService: ElectronNotificationService | null;
|
|
|
|
|
|
|
|
function handleUncaughtException(error: Error) {
|
|
|
|
preventQuit = true;
|
|
|
|
|
|
|
|
// If we haven't got a window we'll assume it's because
|
|
|
|
// we've just launched and haven't created it yet.
|
|
|
|
// It could also be because we're encountering an unhandled
|
|
|
|
// exception on shutdown but that's less likely and since
|
|
|
|
// this only affects the presentation of the crash dialog
|
|
|
|
// it's a safe assumption to make.
|
|
|
|
const isLaunchError = appWindow === null;
|
|
|
|
|
|
|
|
if (appWindow) {
|
|
|
|
appWindow.destroy();
|
|
|
|
appWindow = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
showUncaughtException(isLaunchError, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getUptimeInSeconds() {
|
|
|
|
return (now() - launchTime) / 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
process.on('uncaughtException', (error: Error) => {
|
|
|
|
// error = withSourceMappedStack(error);
|
|
|
|
// reportError(error, getExtraErrorContext());
|
|
|
|
handleUncaughtException(error);
|
|
|
|
});
|
|
|
|
|
|
|
|
let isDuplicateInstance = false;
|
|
|
|
const gotSingleInstanceLock = app.requestSingleInstanceLock();
|
|
|
|
isDuplicateInstance = !gotSingleInstanceLock;
|
|
|
|
let idle: IdleChecker | null;
|
|
|
|
|
|
|
|
app.on(ElectronAppChannel.SecondInstance, (event, args, workingDirectory) => {
|
|
|
|
// Someone tried to run a second instance, we should focus our window.
|
|
|
|
if (appWindow) {
|
|
|
|
if (appWindow.isMinimized()) {
|
|
|
|
appWindow.restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!appWindow.isVisible()) {
|
|
|
|
appWindow.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
appWindow.focus();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (isDuplicateInstance) {
|
|
|
|
app.quit();
|
|
|
|
}
|
|
|
|
|
|
|
|
function createWindow() {
|
2019-11-18 06:02:24 +00:00
|
|
|
const window = new AppWindow(appIconPath);
|
2019-11-11 06:53:39 +00:00
|
|
|
|
|
|
|
if (__DEV__) {
|
|
|
|
// const {
|
|
|
|
// default: installExtension,
|
|
|
|
// REDUX_DEVTOOLS
|
|
|
|
// } = require('electron-devtools-installer');
|
|
|
|
|
|
|
|
import('electron-debug').then(ed => {
|
|
|
|
ed.default({ showDevTools: true });
|
|
|
|
});
|
|
|
|
|
|
|
|
import('electron-devtools-installer').then(edi => {
|
|
|
|
const ChromeLens = {
|
|
|
|
id: 'idikgljglpfilbhaboonnpnnincjhjkd',
|
2019-11-18 06:02:24 +00:00
|
|
|
electron: '>=1.2.1'
|
2019-11-11 06:53:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const extensions = [edi.REDUX_DEVTOOLS, ChromeLens];
|
|
|
|
|
|
|
|
for (const extension of extensions) {
|
|
|
|
try {
|
|
|
|
edi.default(extension);
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
window.onClose(() => {
|
|
|
|
appWindow = null;
|
|
|
|
if (!__DARWIN__ && !preventQuit) {
|
|
|
|
app.quit();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
window.onDidLoad(() => {
|
|
|
|
window.show();
|
|
|
|
|
|
|
|
const fns = onDidLoadFns;
|
|
|
|
onDidLoadFns = null;
|
|
|
|
for (const fn of fns) {
|
|
|
|
fn(window);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
window.load();
|
|
|
|
|
|
|
|
appWindow = window;
|
|
|
|
}
|
|
|
|
|
2019-11-19 07:44:50 +00:00
|
|
|
function createTray() {
|
2019-11-18 06:02:24 +00:00
|
|
|
appTray = new Tray(appIconPath);
|
|
|
|
|
|
|
|
const contextMenu = Menu.buildFromTemplate([
|
|
|
|
{
|
|
|
|
label: '로그아웃',
|
|
|
|
// accelerator: 'Q',
|
|
|
|
// selector: 'terminate:',
|
|
|
|
click: () => {
|
2019-11-25 01:47:44 +00:00
|
|
|
appWindow.show();
|
2019-11-19 07:44:50 +00:00
|
|
|
appWindow.browserWindow.webContents.send(MessengerChannel.Logout);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: '설정',
|
|
|
|
// accelerator: 'Q',
|
|
|
|
// selector: 'terminate:',
|
|
|
|
click: () => {
|
2019-11-21 01:29:19 +00:00
|
|
|
appWindow.show();
|
2019-11-19 07:44:50 +00:00
|
|
|
appWindow.browserWindow.webContents.send(MessengerChannel.ShowSetting);
|
2019-11-18 06:02:24 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{ label: '버전', submenu: [{ label: 'Ver. ' + app.getVersion() }] },
|
|
|
|
{
|
|
|
|
label: '종료',
|
|
|
|
// accelerator: 'Q',
|
|
|
|
// selector: 'terminate:',
|
|
|
|
click: () => {
|
|
|
|
// 메신저에 로그아웃 후 종료
|
|
|
|
appWindow = null;
|
|
|
|
app.exit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]);
|
2019-11-18 07:49:41 +00:00
|
|
|
appTray.setToolTip('UCapMessenger');
|
2019-11-18 06:02:24 +00:00
|
|
|
appTray.setContextMenu(contextMenu);
|
|
|
|
|
|
|
|
appTray.on('click', () => {
|
|
|
|
appWindow.isVisible() ? appWindow.hide() : appWindow.show();
|
|
|
|
});
|
2019-11-19 07:44:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// This method will be called when Electron has finished
|
|
|
|
// initialization and is ready to create browser windows.
|
|
|
|
// Some APIs can only be used after this event occurs.
|
|
|
|
app.on(ElectronAppChannel.Ready, () => {
|
|
|
|
if (isDuplicateInstance) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
readyTime = now() - launchTime;
|
|
|
|
|
|
|
|
createWindow();
|
|
|
|
|
|
|
|
createTray();
|
2019-11-18 06:02:24 +00:00
|
|
|
|
2019-11-11 06:53:39 +00:00
|
|
|
notificationService = new ElectronNotificationService({
|
|
|
|
width: 340,
|
|
|
|
height: 100,
|
|
|
|
padding: 0,
|
|
|
|
borderRadius: 0,
|
|
|
|
// appIcon: iconPath,
|
|
|
|
displayTime: 5000,
|
|
|
|
defaultStyleContainer: {},
|
|
|
|
defaultStyleAppIcon: { display: 'none' },
|
|
|
|
defaultStyleImage: {},
|
|
|
|
defaultStyleClose: {},
|
2019-11-18 06:02:24 +00:00
|
|
|
defaultStyleText: {}
|
2019-11-11 06:53:39 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
notificationService.options.defaultWindow.webPreferences.preload = path.join(
|
|
|
|
__dirname,
|
|
|
|
'resources/notification/preload.js'
|
|
|
|
);
|
|
|
|
|
|
|
|
notificationService.templatePath = path.join(
|
|
|
|
__dirname,
|
|
|
|
'resources/notification/template.html'
|
|
|
|
);
|
|
|
|
|
|
|
|
ipcMain.on('uncaught-exception', (event: IpcMainEvent, error: Error) => {
|
|
|
|
handleUncaughtException(error);
|
|
|
|
});
|
|
|
|
|
|
|
|
ipcMain.on(
|
|
|
|
'send-error-report',
|
|
|
|
(
|
|
|
|
event: IpcMainEvent,
|
|
|
|
{ error, extra }: { error: Error; extra: { [key: string]: string } }
|
|
|
|
) => {}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Quit when all windows are closed.
|
|
|
|
app.on(ElectronAppChannel.WindowAllClosed, () => {
|
|
|
|
// On OS X it is common for applications and their menu bar
|
|
|
|
// to stay active until the user quits explicitly with Cmd + Q
|
|
|
|
if (process.platform !== 'darwin') {
|
|
|
|
app.quit();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
app.on(ElectronAppChannel.Activate, () => {
|
|
|
|
onDidLoad(window => {
|
|
|
|
window.show();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
function onDidLoad(fn: OnDidLoadFn) {
|
|
|
|
if (onDidLoadFns) {
|
|
|
|
onDidLoadFns.push(fn);
|
|
|
|
} else {
|
|
|
|
if (appWindow) {
|
|
|
|
fn(appWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ipcMain.on(UpdaterChannel.Check, (event: IpcMainEvent, ...args: any[]) => {
|
|
|
|
event.returnValue = false;
|
|
|
|
});
|
|
|
|
|
|
|
|
ipcMain.on(FileChannel.ReadFile, (event: IpcMainEvent, ...args: any[]) => {
|
|
|
|
try {
|
|
|
|
fse.readFile(root(args[0]), (err, data) => {
|
|
|
|
if (!!err) {
|
|
|
|
event.returnValue = null;
|
|
|
|
} else {
|
|
|
|
event.returnValue = new Blob([data]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
event.returnValue = null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ipcMain.on(
|
|
|
|
FileChannel.SaveFile,
|
|
|
|
async (event: IpcMainEvent, ...args: any[]) => {
|
|
|
|
try {
|
|
|
|
const buffer: Buffer = args[0];
|
|
|
|
const fileName: string = args[1];
|
2019-11-13 06:28:33 +00:00
|
|
|
const mimeType: string = args[2];
|
2019-11-11 06:53:39 +00:00
|
|
|
let savePath: string = path.join(
|
2019-11-13 06:28:33 +00:00
|
|
|
!!args[3] ? args[3] : DefaultFolder.downloads(),
|
2019-11-11 06:53:39 +00:00
|
|
|
fileName
|
|
|
|
);
|
|
|
|
savePath = await FileUtil.uniqueFileName(savePath);
|
|
|
|
|
|
|
|
fse.writeFile(savePath, buffer, err => {
|
|
|
|
if (!err) {
|
|
|
|
event.returnValue = savePath;
|
|
|
|
} else {
|
|
|
|
event.returnValue = undefined;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
event.returnValue = undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
ipcMain.on(
|
|
|
|
IdleStateChannel.StartCheck,
|
|
|
|
(event: IpcMainEvent, ...args: any[]) => {
|
|
|
|
if (!!idle) {
|
|
|
|
idle.destoryChecker();
|
|
|
|
idle = null;
|
|
|
|
}
|
|
|
|
idle = new IdleChecker(appWindow.browserWindow); // default 10min
|
|
|
|
idle.startChecker();
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
ipcMain.on(
|
|
|
|
NotificationChannel.Notify,
|
|
|
|
(event: IpcMainEvent, ...args: any[]) => {
|
|
|
|
const noti: NotificationRequest = args[0];
|
|
|
|
|
|
|
|
notificationService.notify({
|
|
|
|
title: noti.title,
|
|
|
|
text: noti.contents,
|
|
|
|
image:
|
|
|
|
noti.image ||
|
|
|
|
path.join(__dirname, 'resources/notification/image/img_nophoto_50.png'),
|
|
|
|
sound: noti.useSound
|
|
|
|
? path.join(
|
|
|
|
'file://',
|
|
|
|
__dirname,
|
|
|
|
'resources/notification/sound/messageAlarm.mp3'
|
|
|
|
)
|
|
|
|
: '',
|
2019-11-12 01:38:51 +00:00
|
|
|
onClick: e => {
|
2019-12-04 04:51:50 +00:00
|
|
|
appWindow.browserWindow.flashFrame(false);
|
2019-11-12 01:38:51 +00:00
|
|
|
appWindow.browserWindow.webContents.send(
|
|
|
|
ChatChannel.OpenRoom,
|
|
|
|
noti.roomSeq
|
|
|
|
);
|
|
|
|
appWindow.show();
|
|
|
|
e.close();
|
2019-11-18 06:02:24 +00:00
|
|
|
}
|
2019-11-11 06:53:39 +00:00
|
|
|
});
|
2019-12-04 04:51:50 +00:00
|
|
|
|
|
|
|
appWindow.browserWindow.flashFrame(true);
|
2019-11-11 06:53:39 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
ipcMain.on(
|
|
|
|
NotificationChannel.CloseAllNotify,
|
|
|
|
(event: IpcMainEvent, ...args: any[]) => {
|
2019-12-04 04:51:50 +00:00
|
|
|
appWindow.browserWindow.flashFrame(false);
|
2019-11-11 06:53:39 +00:00
|
|
|
console.log('Channel.closeAllNotify', args);
|
|
|
|
}
|
|
|
|
);
|