'use strict' ;
const { app , protocol , BrowserWindow , dialog , shell , Menu , ipcMain , nativeImage , session } = require ( 'electron' ) ;
// Tray
const tray = require ( './tray' ) ;
// AutoLaunch
var AutoLaunch = require ( 'auto-launch-patched' ) ;
// Configuration
const Config = require ( 'electron-store' ) ;
// Development
const isDev = require ( 'electron-is-dev' ) ;
// Updater
const updater = require ( './updater' ) ;
// File System
var fs = require ( "fs" ) ;
const path = require ( 'path' ) ;
// Disk usage:
// const disk = require('diskusage');
if ( isDev ) app . getVersion = function ( ) { return require ( '../package.json' ) . version ; } ; // FOR DEV ONLY, BECAUSE IN DEV RETURNS ELECTRON'S VERSION
// Initial Config
const config = new Config ( {
defaults : {
always _on _top : false
, hide _menu _bar : false
, tabbar _location : 'top'
, hide _tabbar _labels : false
, window _display _behavior : 'taskbar_tray'
, auto _launch : ! isDev
, flash _frame : true
, window _close _behavior : 'keep_in_tray'
, start _minimized : false
, systemtray _indicator : true
, master _password : false
, dont _disturb : false
, disable _gpu : process . platform === 'linux'
, proxy : false
, proxyHost : ''
, proxyPort : ''
, proxyLogin : ''
, proxyPassword : ''
, locale : 'en'
, enable _hidpi _support : false
, user _agent : ''
, default _service : 'ramboxTab'
, sendStatistics : false
, x : undefined
, y : undefined
, width : 1000
, height : 800
, maximized : false
}
} ) ;
// Fix issues with HiDPI scaling on Windows platform
if ( config . get ( 'enable_hidpi_support' ) && ( process . platform === 'win32' ) ) {
app . commandLine . appendSwitch ( 'high-dpi-support' , 'true' )
app . commandLine . appendSwitch ( 'force-device-scale-factor' , '1' )
}
app . commandLine . appendSwitch ( 'lang' , config . get ( 'locale' ) === 'en' ? 'en-US' : config . get ( 'locale' ) ) ;
// Temporary fix to load Twitter and other websites inside webviews
// Bug related with Electron: https://github.com/electron/electron/issues/25469
app . commandLine . appendSwitch ( 'disable-features' , 'CrossOriginOpenerPolicy' ) ;
// Because we build it using Squirrel, it will assign UserModelId automatically, so we match it here to display notifications correctly.
// https://github.com/electron-userland/electron-builder/issues/362
app . setAppUserModelId ( 'com.grupovrs.ramboxce' ) ;
// Menu
const appMenu = require ( './menu' ) ( config ) ;
// Configure AutoLaunch
let appLauncher ;
if ( ! isDev ) {
appLauncher = new AutoLaunch ( {
name : 'Rambox'
, isHidden : config . get ( 'start_minimized' )
} ) ;
config . get ( 'auto_launch' ) ? appLauncher . enable ( ) : appLauncher . disable ( ) ;
}
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow ;
let isQuitting = false ;
function createWindow ( ) {
// Create the browser window using the state information
mainWindow = new BrowserWindow ( {
title : 'Rambox'
, icon : _ _dirname + '/../resources/Icon.' + ( process . platform === 'linux' ? 'png' : 'ico' )
, backgroundColor : '#FFF'
, x : config . get ( 'x' )
, y : config . get ( 'y' )
, width : config . get ( 'width' )
, height : config . get ( 'height' )
, alwaysOnTop : config . get ( 'always_on_top' )
, autoHideMenuBar : config . get ( 'hide_menu_bar' )
, skipTaskbar : config . get ( 'window_display_behavior' ) === 'show_trayIcon'
, show : ! config . get ( 'start_minimized' )
, acceptFirstMouse : true
, webPreferences : {
enableRemoteModule : true
, plugins : true
, partition : 'persist:rambox'
, nodeIntegration : true
, webviewTag : true
, contextIsolation : false
, spellcheck : false
}
} ) ;
// Check if user has defined a custom User-Agent
if ( config . get ( 'user_agent' ) . length > 0 ) mainWindow . webContents . setUserAgent ( config . get ( 'user_agent' ) ) ;
// Wait for the mainWindow.loadURL(..) and the optional mainWindow.webContents.openDevTools()
// to be finished before minimizing
config . get ( 'start_minimized' ) && mainWindow . webContents . once ( 'did-finish-load' , ( ) => config . get ( 'window_display_behavior' ) === 'show_trayIcon' ? mainWindow . hide ( ) : mainWindow . minimize ( ) ) ;
// Check if the window its outside of the view (ex: multi monitor setup)
const { positionOnScreen } = require ( './utils/positionOnScreen' ) ;
const inBounds = positionOnScreen ( [ config . get ( 'x' ) , config . get ( 'y' ) ] ) ;
if ( inBounds ) {
mainWindow . setPosition ( config . get ( 'x' ) , config . get ( 'y' ) ) ;
} else {
mainWindow . center ( ) ;
}
process . setMaxListeners ( 10000 ) ;
// Open the DevTools.
if ( isDev ) mainWindow . webContents . openDevTools ( ) ;
// and load the index.html of the app.
mainWindow . loadURL ( 'file://' + _ _dirname + '/../index.html' ) ;
Menu . setApplicationMenu ( appMenu ) ;
tray . create ( mainWindow , config ) ;
if ( process . argv . indexOf ( '--without-update' ) === - 1 ) updater . initialize ( mainWindow ) ;
// Open links in default browser
mainWindow . webContents . on ( 'new-window' , function ( e , url , frameName , disposition , options ) {
const protocol = require ( 'url' ) . parse ( url ) . protocol ;
switch ( disposition ) {
case 'new-window' :
e . preventDefault ( ) ;
const win = new BrowserWindow ( options ) ;
if ( config . get ( 'user_agent' ) . length > 0 ) win . webContents . setUserAgent ( config . get ( 'user_agent' ) ) ;
win . once ( 'ready-to-show' , ( ) => win . show ( ) ) ;
win . loadURL ( url ) ;
e . newGuest = win ;
break ;
case 'foreground-tab' :
if ( protocol === 'http:' || protocol === 'https:' || protocol === 'mailto:' ) {
e . preventDefault ( ) ;
shell . openExternal ( url ) ;
}
break ;
default :
break ;
}
} ) ;
mainWindow . webContents . on ( 'will-navigate' , function ( event , url ) {
event . preventDefault ( ) ;
} ) ;
// BrowserWindow events
mainWindow . on ( 'page-title-updated' , ( e , title ) => updateBadge ( title ) ) ;
mainWindow . on ( 'maximize' , function ( e ) { config . set ( 'maximized' , true ) ; } ) ;
mainWindow . on ( 'unmaximize' , function ( e ) { config . set ( 'maximized' , false ) ; } ) ;
mainWindow . on ( 'resize' , function ( e ) { if ( ! mainWindow . isMaximized ( ) ) config . set ( mainWindow . getBounds ( ) ) ; } ) ;
mainWindow . on ( 'move' , function ( e ) { if ( ! mainWindow . isMaximized ( ) ) config . set ( mainWindow . getBounds ( ) ) ; } ) ;
mainWindow . on ( 'app-command' , ( e , cmd ) => {
// Navigate the window back when the user hits their mouse back button
if ( cmd === 'browser-backward' ) mainWindow . webContents . executeJavaScript ( 'if(Ext.cq1("app-main")) Ext.cq1("app-main").getActiveTab().goBack();' ) ;
// Navigate the window forward when the user hits their mouse forward button
if ( cmd === 'browser-forward' ) mainWindow . webContents . executeJavaScript ( 'if(Ext.cq1("app-main")) Ext.cq1("app-main").getActiveTab().goForward();' ) ;
} ) ;
// Emitted when the window is closed.
mainWindow . on ( 'close' , function ( e ) {
if ( ! isQuitting ) {
e . preventDefault ( ) ;
switch ( process . platform ) {
case 'darwin' :
app . hide ( ) ;
break ;
case 'linux' :
case 'win32' :
default :
switch ( config . get ( 'window_close_behavior' ) ) {
case 'keep_in_tray' :
mainWindow . hide ( ) ;
break ;
case 'keep_in_tray_and_taskbar' :
mainWindow . minimize ( ) ;
break ;
case 'quit' :
app . quit ( ) ;
break ;
}
break ;
}
}
} ) ;
mainWindow . on ( 'minimize' , function ( e ) {
if ( config . get ( 'window_display_behavior' ) === 'show_trayIcon' ) mainWindow . setSkipTaskbar ( true ) ;
} ) ;
mainWindow . on ( 'restore' , function ( e ) {
if ( config . get ( 'window_display_behavior' ) === 'show_taskbar' ) mainWindow . setSkipTaskbar ( false ) ;
} ) ;
mainWindow . on ( 'show' , function ( e ) {
if ( config . get ( 'window_display_behavior' ) !== 'show_trayIcon' ) mainWindow . setSkipTaskbar ( false ) ;
} ) ;
mainWindow . on ( 'closed' , function ( e ) {
mainWindow = null ;
} ) ;
mainWindow . once ( 'focus' , ( ) => mainWindow . flashFrame ( false ) ) ;
}
let mainMasterPasswordWindow ;
function createMasterPasswordWindow ( ) {
mainMasterPasswordWindow = new BrowserWindow ( {
backgroundColor : '#0675A0'
, frame : false
, webPreferences : {
nodeIntegration : true
}
} ) ;
// Open the DevTools.
if ( isDev ) mainMasterPasswordWindow . webContents . openDevTools ( ) ;
mainMasterPasswordWindow . loadURL ( 'file://' + _ _dirname + '/../masterpassword.html' ) ;
mainMasterPasswordWindow . on ( 'close' , function ( ) { mainMasterPasswordWindow = null } ) ;
}
function updateBadge ( title ) {
title = title . split ( " - " ) [ 0 ] ; //Discard service name if present, could also contain digits
var messageCount = title . match ( /\d+/g ) ? parseInt ( title . match ( /\d+/g ) . join ( "" ) ) : 0 ;
messageCount = isNaN ( messageCount ) ? 0 : messageCount ;
tray . setBadge ( messageCount , config . get ( 'systemtray_indicator' ) ) ;
if ( process . platform === 'win32' ) {
if ( messageCount === 0 ) return mainWindow . setOverlayIcon ( null , '' ) ;
mainWindow . webContents . send ( 'setBadge' , messageCount ) ;
} else { // macOS & Linux
app . setBadgeCount ( messageCount ) ;
}
if ( messageCount > 0 && ! mainWindow . isFocused ( ) && ! config . get ( 'dont_disturb' ) && config . get ( 'flash_frame' ) ) mainWindow . flashFrame ( true ) ;
}
function formatBytes ( bytes , decimals = 2 ) {
if ( bytes === 0 ) return '0 Bytes' ;
const k = 1024 ;
const dm = decimals < 0 ? 0 : decimals ;
const sizes = [ 'Bytes' , 'KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ] ;
const i = Math . floor ( Math . log ( bytes ) / Math . log ( k ) ) ;
return parseFloat ( ( bytes / Math . pow ( k , i ) ) . toFixed ( dm ) ) + ' ' + sizes [ i ] ;
}
/ * a s y n c f u n c t i o n a v a i l a b l e S p a c e W a t c h D o g ( ) {
// optionally render this information also in rambox window
try {
const { available } = await disk . check ( appPath ) ;
if ( available < 1073741824 ) { // 1 GB
const options = {
type : 'warning' ,
buttons : [ 'OK, quit' ] ,
defaultId : 0 ,
title : ` Running out of disk space! - Rambox shutting down ` ,
detail : ` You've got just ${ formatBytes ( available ) } space left. \n \n Rambox has been frozen to prevent settings corruption. \n \n Once you quit this dialog, Rambox will shutdown. \n \n 1 GB of avalable disk space is required. \n Free up space on partition where Rambox is installed then start the app again. \n \n Rambox path: \n ${ appPath } ` ,
message : ` Running out of disk space! - Rambox shutting down ` ,
} ;
dialog . showMessageBoxSync ( null , options ) ;
app . quit ( ) ;
}
} catch ( err ) {
console . error ( err )
}
} * /
ipcMain . on ( 'setBadge' , function ( event , messageCount , value ) {
mainWindow . setOverlayIcon ( nativeImage . createFromDataURL ( value ) , messageCount . toString ( ) ) ;
} ) ;
ipcMain . on ( 'getConfig' , function ( event , arg ) {
event . returnValue = config . store ;
} ) ;
ipcMain . on ( 'sConfig' , function ( event , values ) {
config . set ( values ) ;
event . returnValue = config ;
} ) ;
ipcMain . on ( 'setConfig' , function ( event , values ) {
config . set ( values ) ;
// hide_menu_bar
mainWindow . setAutoHideMenuBar ( values . hide _menu _bar ) ;
if ( ! values . hide _menu _bar ) mainWindow . setMenuBarVisibility ( true ) ;
// always_on_top
mainWindow . setAlwaysOnTop ( values . always _on _top ) ;
// auto_launch
if ( ! isDev ) values . auto _launch ? appLauncher . enable ( ) : appLauncher . disable ( ) ;
// systemtray_indicator
updateBadge ( mainWindow . getTitle ( ) ) ;
mainWindow . webContents . executeJavaScript ( '(function(a){if(a)a.controller.initialize(a)})(Ext.cq1("app-main"))' ) ;
switch ( values . window _display _behavior ) {
case 'show_taskbar' :
mainWindow . setSkipTaskbar ( false ) ;
tray . destroy ( ) ;
break ;
case 'show_trayIcon' :
mainWindow . setSkipTaskbar ( true ) ;
tray . create ( mainWindow , config ) ;
break ;
case 'taskbar_tray' :
mainWindow . setSkipTaskbar ( false ) ;
tray . create ( mainWindow , config ) ;
break ;
default :
break ;
}
} ) ;
ipcMain . on ( 'sendStatistics' , function ( event ) {
event . returnValue = config . get ( 'sendStatistics' ) ;
} ) ;
ipcMain . on ( 'validateMasterPassword' , function ( event , pass ) {
if ( config . get ( 'master_password' ) === require ( 'crypto' ) . createHash ( 'md5' ) . update ( pass ) . digest ( 'hex' ) ) {
createWindow ( ) ;
mainMasterPasswordWindow . close ( ) ;
event . returnValue = true ;
}
event . returnValue = false ;
} ) ;
// Handle Service Notifications
ipcMain . on ( 'setServiceNotifications' , function ( event , partition , op ) {
if ( partition === null ) return ;
session . fromPartition ( partition ) . setPermissionRequestHandler ( function ( webContents , permission , callback ) {
if ( permission === 'notifications' ) return callback ( op ) ;
callback ( true )
} ) ;
} ) ;
ipcMain . on ( 'setDontDisturb' , function ( event , arg ) {
config . set ( 'dont_disturb' , arg ) ;
} ) ;
// Reload app
ipcMain . on ( 'reloadApp' , function ( event ) {
mainWindow . reload ( ) ;
} ) ;
// Relaunch app
ipcMain . on ( 'relaunchApp' , function ( event ) {
app . relaunch ( ) ;
app . exit ( 0 ) ;
} ) ;
const shouldQuit = app . requestSingleInstanceLock ( ) ;
if ( ! shouldQuit ) {
app . quit ( ) ;
return ;
}
app . on ( 'second-instance' , ( event , commandLine , workingDirectory ) => {
// Someone tried to run a second instance, we should focus our window.
if ( mainWindow ) {
if ( mainWindow . isMinimized ( ) ) mainWindow . restore ( ) ;
mainWindow . focus ( ) ;
mainWindow . show ( ) ;
mainWindow . setSkipTaskbar ( false ) ;
if ( app . dock && app . dock . show ) app . dock . show ( ) ;
}
} ) ;
// ALLOWED URLS POPUPS
let allowPopUp = [
'feedly.com/v3/auth/' ,
'identity.linuxfoundation.org/cas/login' ,
'auth.missiveapp.com' ,
'accounts.google.com/AccountChooser' ,
'facebook.com/v3.1/dialog/oauth?' ,
'accounts.google.com/o/oauth2' ,
'app.slack.com/files/import/gdrive' ,
'spikenow.com/s/account' ,
'app.mixmax.com/_oauth/google' ,
'officeapps.live.com' ,
'dropbox.com/profile_services/start_auth_flow' ,
'facebook.com/v3.2/dialog/oauth?' ,
'notion.so/googlepopupredirect' ,
'zoom.us/office365' ,
'figma.com/start_google_sso' ,
'mail.google.com/mail' ,
'app.slack.com/free-willy/' ,
'messenger.com/videocall' ,
'api.moo.do' ,
'manychat.com/fb?popup' ,
'=?print=true' // esta ultima checkea como anda imprimir un pedf desde gmail, si no va bie sacala
] ;
app . on ( 'web-contents-created' , ( webContentsCreatedEvent , contents ) => {
if ( contents . getType ( ) !== 'webview' ) return ;
// Block some Deep links to prevent that open its app (Ex: Slack)
contents . on ( 'will-navigate' , ( event , url ) => url . substring ( 0 , 8 ) === 'slack://' && event . preventDefault ( ) ) ;
// New Window handler
contents . on ( 'new-window' , ( event , url , frameName , disposition , options , additionalFeatures , referrer , postBody ) => {
// If the url is about:blank we allow the window and handle it in 'did-create-window'
if ( [ 'about:blank' , 'about:blank#blocked' ] . includes ( url ) ) {
event . preventDefault ( ) ;
Object . assign ( options , { show : false } ) ;
const win = new BrowserWindow ( options ) ;
win . center ( ) ;
let once = false ;
win . webContents . on ( 'will-navigate' , ( e , nextURL ) => {
if ( once ) return ;
if ( [ 'about:blank' , 'about:blank#blocked' ] . includes ( nextURL ) ) return ;
once = true ;
let allow = false ;
allowPopUp . forEach ( url => nextURL . indexOf ( url ) > - 1 && ( allow = true ) ) ;
// If the url is in aboutBlankOnlyWindow we handle this as a popup window
if ( allow ) return win . show ( ) ;
shell . openExternal ( nextURL ) ;
win . close ( )
} )
event . newGuest = win ;
return ;
}
// We check if url is in the allowPopUpLoginURLs or allowForegroundTabURLs in Firebase to open a as a popup,
// if it is not we send this to the app
let allow = false ;
allowPopUp . forEach ( allowed => url . indexOf ( allowed ) > - 1 && ( allow = true ) ) ;
if ( allow ) return ;
shell . openExternal ( url ) ;
event . preventDefault ( ) ;
} ) ;
contents . on ( 'did-create-window' , ( win , details ) => {
// Here we center the new window.
win . center ( ) ;
// The following code is for handling the about:blank cases only.
if ( ! [ 'about:blank' , 'about:blank#blocked' ] . includes ( details . url ) ) return ;
let once = false ;
win . webContents . on ( 'will-navigate' , ( e , nextURL ) => {
if ( once ) return ;
if ( [ 'about:blank' , 'about:blank#blocked' ] . includes ( nextURL ) ) return ;
once = true ;
let allow = false ;
allowPopUp . forEach ( url => nextURL . indexOf ( url ) > - 1 && ( allow = true ) ) ;
// If the url is in aboutBlankOnlyWindow we handle this as a popup window
if ( allow ) return win . show ( ) ;
shell . openExternal ( url ) ;
win . close ( ) ;
} ) ;
} ) ;
} ) ;
// Code for downloading images as temporal files
// Credit: Ghetto Skype (https://github.com/stanfieldr/ghetto-skype)
const tmp = require ( 'tmp' ) ;
const mime = require ( 'mime' ) ;
var imageCache = { } ;
ipcMain . on ( 'image:download' , function ( event , url , partition ) {
let file = imageCache [ url ] ;
if ( file ) {
if ( file . complete ) {
shell . openItem ( file . path ) ;
}
// Pending downloads intentionally do not proceed
return ;
}
let tmpWindow = new BrowserWindow ( {
show : false
, webPreferences : {
partition : partition
}
} ) ;
if ( config . get ( 'user_agent' ) . length > 0 ) tmpWindow . webContents . setUserAgent ( config . get ( 'user_agent' ) ) ;
tmpWindow . webContents . session . once ( 'will-download' , ( event , downloadItem ) => {
imageCache [ url ] = file = {
path : tmp . tmpNameSync ( ) + '.' + mime . extension ( downloadItem . getMimeType ( ) )
, complete : false
} ;
downloadItem . setSavePath ( file . path ) ;
downloadItem . once ( 'done' , ( ) => {
tmpWindow . destroy ( ) ;
tmpWindow = null ;
shell . openItem ( file . path ) ;
file . complete = true ;
} ) ;
} ) ;
tmpWindow . webContents . downloadURL ( url ) ;
} ) ;
// Hangouts
ipcMain . on ( 'image:popup' , function ( event , url , partition ) {
let tmpWindow = new BrowserWindow ( {
width : mainWindow . getBounds ( ) . width
, height : mainWindow . getBounds ( ) . height
, parent : mainWindow
, icon : _ _dirname + '/../resources/Icon.ico'
, backgroundColor : '#FFF'
, autoHideMenuBar : true
, skipTaskbar : true
, webPreferences : {
partition : partition
}
} ) ;
if ( config . get ( 'user_agent' ) . length > 0 ) tmpWindow . webContents . setUserAgent ( config . get ( 'user_agent' ) ) ;
tmpWindow . maximize ( ) ;
tmpWindow . loadURL ( url ) ;
} ) ;
ipcMain . on ( 'toggleWin' , function ( event , allwaysShow ) {
if ( config . get ( 'window_display_behavior' ) !== 'show_trayIcon' ) mainWindow . setSkipTaskbar ( false ) ;
if ( ! mainWindow . isMinimized ( ) && mainWindow . isMaximized ( ) && mainWindow . isVisible ( ) ) { // Maximized
! allwaysShow ? mainWindow . close ( ) : mainWindow . show ( ) ;
} else if ( mainWindow . isMinimized ( ) && ! mainWindow . isMaximized ( ) && ! mainWindow . isVisible ( ) ) { // Minimized
if ( process . platform === 'linux' ) {
mainWindow . minimize ( ) ;
mainWindow . restore ( ) ;
mainWindow . focus ( ) ;
return
}
mainWindow . restore ( ) ;
} else if ( ! mainWindow . isMinimized ( ) && ! mainWindow . isMaximized ( ) && mainWindow . isVisible ( ) ) { // Windowed mode
! allwaysShow ? mainWindow . close ( ) : mainWindow . show ( ) ;
} else if ( mainWindow . isMinimized ( ) && ! mainWindow . isMaximized ( ) && mainWindow . isVisible ( ) ) { // Closed to taskbar
if ( process . platform === 'linux' ) {
mainWindow . minimize ( ) ;
mainWindow . restore ( ) ;
mainWindow . focus ( ) ;
return
}
mainWindow . restore ( ) ;
} else if ( ! mainWindow . isMinimized ( ) && mainWindow . isMaximized ( ) && ! mainWindow . isVisible ( ) ) { // Closed maximized to tray
mainWindow . show ( ) ;
} else if ( ! mainWindow . isMinimized ( ) && ! mainWindow . isMaximized ( ) && ! mainWindow . isVisible ( ) ) { // Closed windowed to tray
mainWindow . show ( ) ;
} else if ( mainWindow . isMinimized ( ) && ! mainWindow . isMaximized ( ) && ! mainWindow . isVisible ( ) ) { // Closed minimized to tray
mainWindow . show ( ) ;
} else {
if ( process . platform === 'linux' ) {
mainWindow . minimize ( ) ;
mainWindow . maximize ( ) ;
mainWindow . focus ( ) ;
return
}
mainWindow . restore ( ) ;
}
} ) ;
// Proxy
if ( config . get ( 'proxy' ) ) {
app . commandLine . appendSwitch ( 'proxy-server' , config . get ( 'proxyHost' ) + ':' + config . get ( 'proxyPort' ) ) ;
app . on ( 'login' , ( event , webContents , request , authInfo , callback ) => {
if ( ! authInfo . isProxy )
return ;
event . preventDefault ( ) ;
callback ( config . get ( 'proxyLogin' ) , config . get ( 'proxyPassword' ) )
} )
}
// Disable GPU Acceleration for Linux
// to prevent White Page bug
// https://github.com/electron/electron/issues/6139
// https://github.com/saenzramiro/rambox/issues/181
if ( config . get ( 'disable_gpu' ) ) app . disableHardwareAcceleration ( ) ;
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app . on ( 'ready' , function ( ) {
config . get ( 'master_password' ) ? createMasterPasswordWindow ( ) : createWindow ( ) ;
// setInterval(availableSpaceWatchDog, 1000 * 60);
} ) ;
// Quit when all windows are closed.
app . on ( 'window-all-closed' , function ( ) {
// 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 ( ) ;
}
} ) ;
// Only macOS: On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
app . on ( 'activate' , function ( ) {
if ( mainWindow === null && mainMasterPasswordWindow === null ) {
config . get ( 'master_password' ) ? createMasterPasswordWindow ( ) : createWindow ( ) ;
}
if ( mainWindow ) {
mainWindow . show ( ) ;
}
} ) ;
app . on ( 'before-quit' , function ( ) {
isQuitting = true ;
} ) ;