import { app , BrowserView , BrowserWindow , dialog , nativeImage , screen , shell , WebContents , } from 'electron' ; import electron = require ( 'electron' ) ; import fetch from 'electron-fetch' ; import * as filesize from 'filesize' ; import * as fs from 'fs' ; import * as path from 'path' ; import { format , parse } from 'url' ; import { apiName } from '../common/api-interface' ; import { isDevEnv , isLinux , isMac , isNodeEnv , isWindowsOS , } from '../common/env' ; import { i18n , LocaleType } from '../common/i18n' ; import { logger } from '../common/logger' ; import { getGuid } from '../common/utils' ; import { whitelistHandler } from '../common/whitelist-handler' ; import { autoLaunchInstance } from './auto-launch-controller' ; import { CloudConfigDataTypes , config , IConfig , ICustomRectangle , } from './config-handler' ; import { downloadHandler , IDownloadItem } from './download-handler' ; import { memoryMonitor } from './memory-monitor' ; import { screenSnippet } from './screen-snippet-handler' ; import { updateAlwaysOnTop } from './window-actions' ; import { DEFAULT_HEIGHT , DEFAULT_WIDTH , ICustomBrowserView , ICustomBrowserWindow , TITLE_BAR_HEIGHT , windowHandler , } from './window-handler' ; import { notification } from '../renderer/notification' ; import { mainEvents } from './main-event-handler' ; interface IStyles { name : styleNames ; content : string ; } enum styleNames { titleBar = 'title-bar' , snackBar = 'snack-bar' , messageBanner = 'message-banner' } const checkValidWindow = true ; const { ctWhitelist } = config . getConfigFields ( [ 'ctWhitelist' ] ) ; const networkStatusCheckInterval = 10 * 1000 ; let networkStatusCheckIntervalId ; let isNetworkMonitorInitialized = false ; const styles = [ ] ; const DOWNLOAD_MANAGER_NAMESPACE = 'DownloadManager' ; const TITLE_BAR_EVENTS = [ 'maximize' , 'unmaximize' , 'enter-full-screen' , 'leave-full-screen' , ] ; param window { BrowserWindow } return export const windowExists = ( window ) : boolean => ! ! window && typeof window . isDestroyed === 'function' && ! window . isDestroyed ( ) ; param view { BrowserView } return export const viewExists = ( view ) : boolean => ! ! view && typeof view . webContents . isDestroyed === 'function' && ! view . webContents . isDestroyed ( ) ; param browserWindow param isPopOutWindow export const preventWindowNavigation = ( browserWindow , isPopOutWindow = false ) : void => { if ( ! browserWindow || ! windowExists ( browserWindow ) ) { return ; } logger . info ( `window-utils: preventing window from navigating!` , isPopOutWindow ) ; const listener = async ( e , winUrl ) => { if ( ! winUrl . startsWith ( 'http' || 'https' ) ) { logger . error ( `window-utils: ${ winUrl } doesn't start with http or https, so, not navigating!` ) ; e . preventDefault ( ) ; return ; } if ( ! isPopOutWindow ) { const isValid = whitelistHandler . isWhitelisted ( winUrl ) ; if ( ! isValid ) { e . preventDefault ( ) ; if ( browserWindow && windowExists ( browserWindow ) ) { const response = await dialog . showMessageBox ( browserWindow , { type : 'warning' , buttons : [ 'OK' ] , title : i18n . t ( 'Not Allowed' ) ( ) , message : `${ i18n . t ( `Sorry, you are not allowed to access this website` ) ( ) } (${ winUrl }), ${ i18n . t ( 'please contact your administrator for more details' ) ( ) }` , } ) ; logger . info ( `window-utils: received ${ response } response from dialog` ) ; } } windowHandler . execCmd ( windowHandler . screenShareIndicatorFrameUtil , [ ] ) ; } if ( browserWindow . isDestroyed ( ) || browserWindow . webContents . isDestroyed ( ) || winUrl === browserWindow . webContents . getURL ( ) ) { return ; } } ; browserWindow . webContents . on ( 'will-navigate' , listener ) ; browserWindow . once ( 'close' , ( ) => { browserWindow . webContents . removeListener ( 'will-navigate' , listener ) ; } ) ; } ; param componentName param opts param shouldFocus { boolean } export const createComponentWindow = ( componentName , opts ? , shouldFocus = true ) : BrowserWindow => { const options = { center : true , height : 300 , maximizable : false , minimizable : false , resizable : false , show : false , width : 300 , ... opts , webPreferences : { sandbox : ! isNodeEnv , nodeIntegration : isNodeEnv , preload : path . join ( __dirname , '../renderer/_preload-component.js' ) , devTools : isDevEnv , } , } ; const browserWindow = new BrowserWindow ( options ) as ICustomBrowserWindow ; if ( shouldFocus ) { browserWindow . once ( 'ready-to-show' , ( ) => { if ( ! browserWindow || ! windowExists ( browserWindow ) ) { return ; } browserWindow . show ( ) ; } ) ; } browserWindow . webContents . once ( 'did-finish-load' , ( ) => { if ( ! browserWindow || ! windowExists ( browserWindow ) ) { return ; } browserWindow . webContents . send ( 'page-load' , { locale : i18n . getLocale ( ) , resource : i18n . loadedResources , } ) ; } ) ; browserWindow . setMenu ( null as any ) ; const targetUrl = format ( { pathname : require . resolve ( '../renderer/react-window.html' ) , protocol : 'file' , query : { componentName , locale : i18n . getLocale ( ) , } , slashes : true , } ) ; browserWindow . loadURL ( targetUrl ) ; preventWindowNavigation ( browserWindow ) ; return browserWindow ; } ; param count { number } export const showBadgeCount = ( count ) : void => { if ( typeof count !== 'number' ) { logger . warn ( `window-utils: badgeCount: invalid func arg, must be a number: ${ count }` ) ; return ; } logger . info ( `window-utils: updating badge count to ${ count }!` ) ; if ( isMac || isLinux ) { app . setBadgeCount ( Math . min ( 1e8 , count ) ) ; return ; } const mainWindow = windowHandler . getMainWindow ( ) ; const mainWebContents = windowHandler . getMainWebContents ( ) ; if ( ! mainWebContents || mainWebContents . isDestroyed ( ) ) { return ; } if ( count > 0 ) { mainWebContents . send ( 'create-badge-data-url' , { count } ) ; return ; } if ( ! mainWindow || ! windowExists ( mainWindow ) ) { return ; } mainWindow . setOverlayIcon ( null , '' ) ; } ; param dataUrl param count export const setDataUrl = ( dataUrl , count ) : void => { const mainWindow = windowHandler . getMainWindow ( ) ; if ( mainWindow && dataUrl && count ) { const img = nativeImage . createFromDataURL ( dataUrl ) ; const desc = 'Symphony has ' + count + ' unread messages' ; mainWindow . setOverlayIcon ( img , desc ) ; } } ; param { BrowserWindow } browserWin return { Boolean } export const isValidWindow = ( browserWin ) : boolean => { if ( ! checkValidWindow ) { return true ; } let result = false ; if ( browserWin && ! browserWin . isDestroyed ( ) ) { result = windowHandler . hasWindow ( browserWin ) ; } if ( ! result ) { logger . warn ( `window-utils: invalid window try to perform action, ignoring action` ) ; } return result ; } ; return { Boolean } param webContents export const isValidView = ( webContents ) : boolean => { if ( ! checkValidWindow ) { return true ; } let result = false ; if ( webContents && ! webContents . isDestroyed ( ) ) { result = windowHandler . hasView ( webContents ) ; } if ( ! result ) { logger . warn ( `window-utils: invalid window try to perform action, ignoring action` ) ; } return result ; } ; param locale { LocaleType } export const updateLocale = async ( locale ) : Promise < void > => { logger . info ( `window-utils: updating locale to ${ locale }!` ) ; i18n . setLocale ( locale ) ; const appMenu = windowHandler . appMenu ; if ( appMenu ) { logger . info ( `window-utils: updating app menu with locale ${ locale }!` ) ; appMenu . update ( locale ) ; } notification . cleanUpInactiveNotification ( ) ; if ( i18n . isValidLocale ( locale ) ) { await config . updateUserConfig ( { locale } ) ; } } ; /**
 * Displays a popup menu
 */ export const showPopupMenu = ( opts ) : void => { const browserWindow = windowHandler . getMainWindow ( ) || windowHandler . getWelcomeScreenWindow ( ) ; if ( browserWindow && windowExists ( browserWindow ) && isValidWindow ( browserWindow ) ) { const coordinates = windowHandler . isCustomTitleBar ? { x : 20 , y : 15 } : { x : 10 , y : - 20 } ; const { x , y } = browserWindow . isFullScreen ( ) ? { x : 0 , y : 0 } : coordinates ; const popupOpts = { window : browserWindow , x , y } ; const appMenu = windowHandler . appMenu ; if ( appMenu ) { appMenu . popupMenu ( { ... popupOpts , ... opts } ) ; } } } ; param windowName { string } export const sanitize = ( windowName ) : void => { const mainWindow = windowHandler . getMainWindow ( ) ; if ( mainWindow && windowName === mainWindow . winName ) { showBadgeCount ( 0 ) ; if ( ! isMac || ! isLinux ) { logger . info ( 'window-utils: Terminating screen snippet and screen share indicator frame utils' ) ; screenSnippet . killChildProcess ( ) ; windowHandler . execCmd ( windowHandler . screenShareIndicatorFrameUtil , [ ] ) ; } windowHandler . closeAllWindows ( ) ; } } ; param winPos { Electron . Rectangle } param defaultWidth param defaultHeight return { x ? export const getBounds = ( winPos , defaultWidth , defaultHeight ) : Partial < Electron . Rectangle > => { logger . info ( 'window-utils: getBounds, winPos: ' + JSON . stringify ( winPos ) ) ; if ( ! winPos || ! winPos . x || ! winPos . y || ! winPos . width || ! winPos . height ) { return { width : defaultWidth , height : defaultHeight } ; } const displays = screen . getAllDisplays ( ) ; for ( let i = 0 , len = displays . length ; i < len ; i ++ ) { const bounds = displays [ i ] . bounds ; logger . info ( 'window-utils: getBounds, bounds: ' + JSON . stringify ( bounds ) ) ; if ( winPos . x >= bounds . x && winPos . y >= bounds . y && winPos . x + winPos . width <= bounds . x + bounds . width && winPos . y + winPos . height <= bounds . y + bounds . height ) { return winPos ; } } const display = screen . getDisplayMatching ( winPos as electron . Rectangle ) ; if ( display ) { let windowWidth = defaultWidth ; if ( display . workArea . width < defaultWidth ) { windowWidth = display . workArea . width ; } let windowHeight = defaultHeight ; if ( display . workArea . height < defaultHeight ) { windowHeight = display . workArea . height ; } const windowX = display . workArea . x + display . workArea . width / 2 - windowWidth / 2 ; const windowY = display . workArea . y + display . workArea . height / 2 - windowHeight / 2 ; return { x : windowX , y : windowY , width : windowWidth , height : windowHeight } ; } return { width : defaultWidth , height : defaultHeight } ; } ; param type param filePath export const downloadManagerAction = async ( type , filePath ) : Promise < void > => { const focusedWindow = BrowserWindow . getFocusedWindow ( ) ; const message = i18n . t ( 'The file you are trying to open cannot be found in the specified path.' , DOWNLOAD_MANAGER_NAMESPACE ) ( ) ; const title = i18n . t ( 'File not Found' , DOWNLOAD_MANAGER_NAMESPACE ) ( ) ; if ( ! focusedWindow || ! windowExists ( focusedWindow ) ) { return ; } if ( type === 'open' ) { const fileExists = fs . existsSync ( `${ filePath }` ) ; let openFileResponse ; if ( fileExists ) { openFileResponse = await shell . openPath ( filePath ) ; } if ( openFileResponse !== '' && focusedWindow && ! focusedWindow . isDestroyed ( ) ) { dialog . showMessageBox ( focusedWindow , { message , title , type : 'error' , } ) ; } return ; } if ( fs . existsSync ( filePath ) ) { shell . showItemInFolder ( filePath ) ; } else { dialog . showMessageBox ( focusedWindow , { message , title , type : 'error' , } ) ; } } ; param _event param item { Electron . DownloadItem } param webContents { WeContents } export const handleDownloadManager = ( _event , item , webContents ) => { item . once ( 'done' , ( _e , state ) => { if ( state === 'completed' ) { const savePathSplit = item . getSavePath ( ) ?. split ( path . sep ) ; const data = { _id : getGuid ( ) , savedPath : item . getSavePath ( ) || '' , total : filesize ( item . getTotalBytes ( ) || 0 ) , fileName : savePathSplit [ savePathSplit . length - 1 ] || 'No name' , } ; logger . info ( 'window-utils: Download completed, informing download manager' ) ; webContents . send ( 'downloadCompleted' , data ) ; downloadHandler . onDownloadSuccess ( data ) ; } else { logger . info ( 'window-utils: Download failed, informing download manager' ) ; downloadHandler . onDownloadFailed ( ) ; } } ) ; item . on ( 'updated' , ( _e , state ) => { if ( state === 'interrupted' ) { logger . info ( 'window-utils: Download is interrupted but can be resumed' ) ; } else if ( state === 'progressing' ) { if ( item . isPaused ( ) ) { logger . info ( 'window-utils: Download is paused' ) ; } else { logger . info ( `window-utils: Received bytes: ${ item . getReceivedBytes ( ) }` ) ; } } } ) ; } ; param mainWebContents { WebContents } const readAndInsertCSS = async ( mainWebContents ) : Promise < IStyles [ ] | void > => { if ( mainWebContents && ! mainWebContents . isDestroyed ( ) ) { return styles . map ( ( { content } ) => mainWebContents . insertCSS ( content ) ) ; } } ; param mainView { WebContents } param isCustomTitleBar { boolean } export const injectStyles = async ( mainView , isCustomTitleBar ) : Promise < IStyles [ ] | void > => { if ( isCustomTitleBar ) { const index = styles . findIndex ( ( { name } ) => name === styleNames . titleBar ) ; if ( index === - 1 ) { let titleBarStylesPath ; const stylesFileName = path . join ( 'config' , 'titleBarStyles.css' ) ; if ( isDevEnv ) { titleBarStylesPath = path . join ( app . getAppPath ( ) , stylesFileName ) ; } else { const execPath = path . dirname ( app . getPath ( 'exe' ) ) ; titleBarStylesPath = path . join ( execPath , stylesFileName ) ; } if ( fs . existsSync ( titleBarStylesPath ) ) { styles . push ( { name : styleNames . titleBar , content : fs . readFileSync ( titleBarStylesPath , 'utf8' ) . toString ( ) , } ) ; } else { const stylePath = path . join ( __dirname , '..' , '/renderer/styles/title-bar.css' ) ; styles . push ( { name : styleNames . titleBar , content : fs . readFileSync ( stylePath , 'utf8' ) . toString ( ) , } ) ; } } } if ( styles . findIndex ( ( { name } ) => name === styleNames . snackBar ) === - 1 ) { styles . push ( { name : styleNames . snackBar , content : fs . readFileSync ( path . join ( __dirname , '..' , '/renderer/styles/snack-bar.css' ) , 'utf8' ) . toString ( ) , } ) ; } if ( styles . findIndex ( ( { name } ) => name === styleNames . messageBanner ) === - 1 ) { styles . push ( { name : styleNames . messageBanner , content : fs . readFileSync ( path . join ( __dirname , '..' , '/renderer/styles/message-banner.css' ) , 'utf8' ) . toString ( ) , } ) ; } await readAndInsertCSS ( mainView ) ; return ; } ; param request { any } param callback { ( verificationResult : number ) => void } export const handleCertificateProxyVerification = ( request , callback ) : void => { const { hostname : hostUrl , errorCode } = request ; if ( errorCode === 0 ) { return callback ( 0 ) ; } const { tld , domain } = whitelistHandler . parseDomain ( hostUrl ) ; const host = domain + tld ; if ( ctWhitelist && Array . isArray ( ctWhitelist ) && ctWhitelist . length > 0 && ctWhitelist . indexOf ( host ) > - 1 ) { return callback ( 0 ) ; } return callback ( - 2 ) ; } ; param window { ICustomBrowserWindow } param url export const isSymphonyReachable = ( window , url ) => { if ( networkStatusCheckIntervalId ) { return ; } if ( ! window || ! windowExists ( window ) ) { return ; } networkStatusCheckIntervalId = setInterval ( ( ) => { const { hostname , protocol } = parse ( url ) ; if ( ! hostname || ! protocol ) { return ; } const podUrl = `${ protocol }//${ hostname }` ; logger . info ( `window-utils: checking to see if pod ${ podUrl } is reachable!` ) ; fetch ( podUrl , { method : 'GET' } ) . then ( async ( rsp ) => { if ( rsp . status === 200 && windowHandler . isOnline ) { logger . info ( `window-utils: pod ${ podUrl } is reachable, loading main window!` ) ; await windowHandler . reloadSymphony ( ) ; if ( networkStatusCheckIntervalId ) { clearInterval ( networkStatusCheckIntervalId ) ; networkStatusCheckIntervalId = null ; } return ; } logger . warn ( `window-utils: POD is down! statusCode: ${ rsp . status }, is online: ${ windowHandler . isOnline }` ) ; } ) . catch ( ( error ) => { logger . error ( `window-utils: Network status check: No active network connection ${ error }` ) ; } ) ; } , networkStatusCheckInterval ) ; } ; param browserWindow { ICustomBrowserWindow } export const reloadWindow = ( browserWindow ) => { if ( ! browserWindow || ! windowExists ( browserWindow ) ) { return ; } const windowName = browserWindow . winName ; const mainWebContents = windowHandler . getMainWebContents ( ) ; if ( windowName === apiName . mainWindowName && mainWebContents && ! mainWebContents . isDestroyed ( ) ) { logger . info ( `window-utils: reloading the main window` ) ; mainWebContents . reload ( ) ; windowHandler . closeAllWindows ( ) ; windowHandler . execCmd ( windowHandler . screenShareIndicatorFrameUtil , [ ] ) ; return ; } if ( mainWebContents && ! mainWebContents . isDestroyed ( ) ) { logger . info ( `window-handler: reloading the window` , { windowName } ) ; const bounds = browserWindow . getBounds ( ) ; mainWebContents . send ( 'restart-floater' , { windowName , bounds } ) ; } } ; returns export const zoomIn = ( ) => { const focusedWindow = BrowserWindow . getFocusedWindow ( ) ; if ( ! focusedWindow || ! windowExists ( focusedWindow ) ) { return ; } if ( focusedWindow . getTitle ( ) === 'Screen Sharing Indicator - Symphony' ) { return ; } if ( ( focusedWindow as ICustomBrowserWindow ) . winName && ( focusedWindow as ICustomBrowserWindow ) . winName === apiName . notificationWindowName ) { return ; } let { webContents } = focusedWindow ; if ( ( focusedWindow as ICustomBrowserWindow ) . winName === apiName . mainWindowName ) { const mainWebContents = windowHandler . getMainWebContents ( ) ; if ( mainWebContents && ! mainWebContents . isDestroyed ( ) ) { webContents = mainWebContents ; } } if ( windowHandler . isMana ) { const zoomFactor = webContents . getZoomFactor ( ) ; if ( zoomFactor < 1.5 ) { if ( zoomFactor < 0.7 ) { webContents . setZoomFactor ( 0.7 ) ; } else if ( zoomFactor >= 0.7 && zoomFactor < 0.8 ) { webContents . setZoomFactor ( 0.8 ) ; } else if ( zoomFactor >= 0.8 && zoomFactor < 0.9 ) { webContents . setZoomFactor ( 0.9 ) ; } else if ( zoomFactor >= 0.9 && zoomFactor < 1.0 ) { webContents . setZoomFactor ( 1.0 ) ; } else if ( zoomFactor >= 1.0 && zoomFactor < 1.1 ) { webContents . setZoomFactor ( 1.1 ) ; } else if ( zoomFactor >= 1.1 && zoomFactor < 1.25 ) { webContents . setZoomFactor ( 1.25 ) ; } else if ( zoomFactor >= 1.25 && zoomFactor < 1.5 ) { webContents . setZoomFactor ( 1.5 ) ; } } } else { const currentZoomLevel = webContents . getZoomLevel ( ) ; webContents . setZoomLevel ( currentZoomLevel + 0.5 ) ; } } ; returns export const zoomOut = ( ) => { const focusedWindow = BrowserWindow . getFocusedWindow ( ) ; if ( ! focusedWindow || ! windowExists ( focusedWindow ) ) { return ; } if ( focusedWindow . getTitle ( ) === 'Screen Sharing Indicator - Symphony' ) { return ; } if ( ( focusedWindow as ICustomBrowserWindow ) . winName && ( focusedWindow as ICustomBrowserWindow ) . winName === apiName . notificationWindowName ) { return ; } let { webContents } = focusedWindow ; if ( ( focusedWindow as ICustomBrowserWindow ) . winName === apiName . mainWindowName ) { const mainWebContents = windowHandler . getMainWebContents ( ) ; if ( mainWebContents && ! mainWebContents . isDestroyed ( ) ) { webContents = mainWebContents ; } } if ( windowHandler . isMana ) { const zoomFactor = webContents . getZoomFactor ( ) ; if ( zoomFactor > 0.7 ) { if ( zoomFactor > 1.5 ) { webContents . setZoomFactor ( 1.5 ) ; } else if ( zoomFactor > 1.25 && zoomFactor <= 1.5 ) { webContents . setZoomFactor ( 1.25 ) ; } else if ( zoomFactor > 1.1 && zoomFactor <= 1.25 ) { webContents . setZoomFactor ( 1.1 ) ; } else if ( zoomFactor > 1.0 && zoomFactor <= 1.1 ) { webContents . setZoomFactor ( 1.0 ) ; } else if ( zoomFactor > 0.9 && zoomFactor <= 1.0 ) { webContents . setZoomFactor ( 0.9 ) ; } else if ( zoomFactor > 0.8 && zoomFactor <= 0.9 ) { webContents . setZoomFactor ( 0.8 ) ; } else if ( zoomFactor > 0.7 && zoomFactor <= 0.8 ) { webContents . setZoomFactor ( 0.7 ) ; } } } else { const currentZoomLevel = webContents . getZoomLevel ( ) ; webContents . setZoomLevel ( currentZoomLevel - 0.5 ) ; } } ; returns export const resetZoomLevel = ( ) => { const focusedWindow = BrowserWindow . getFocusedWindow ( ) ; if ( ! focusedWindow || ! windowExists ( focusedWindow ) ) { return ; } if ( ( focusedWindow as ICustomBrowserWindow ) . winName && ( focusedWindow as ICustomBrowserWindow ) . winName === apiName . notificationWindowName ) { return ; } let { webContents } = focusedWindow ; if ( ( focusedWindow as ICustomBrowserWindow ) . winName === apiName . mainWindowName ) { const mainWebContents = windowHandler . getMainWebContents ( ) ; if ( mainWebContents && ! mainWebContents . isDestroyed ( ) ) { webContents = mainWebContents ; } } webContents . setZoomLevel ( 0 ) ; } ; param browserWindow { ICustomBrowserWindow } export const didVerifyAndRestoreWindow = ( browserWindow ) : boolean => { if ( ! browserWindow || ! windowExists ( browserWindow ) ) { return false ; } if ( browserWindow . isMinimized ( ) ) { browserWindow . restore ( ) ; } browserWindow . focus ( ) ; return true ; } ; param windowName { String } export const getWindowByName = ( windowName ) : BrowserWindow | undefined => { const allWindows = BrowserWindow . getAllWindows ( ) ; return allWindows . find ( ( window ) => { return ( window as ICustomBrowserWindow ) . winName === windowName ; } ) ; } ; export const updateFeaturesForCloudConfig = async ( ) : Promise < void > => { const { alwaysOnTop : isAlwaysOnTop , launchOnStartup , memoryRefresh , memoryThreshold , } = config . getConfigFields ( [ 'launchOnStartup' , 'alwaysOnTop' , 'memoryRefresh' , 'memoryThreshold' , ] ) as IConfig ; const mainWebContents = windowHandler . getMainWebContents ( ) ; await updateAlwaysOnTop ( isAlwaysOnTop === CloudConfigDataTypes . ENABLED , false , false ) ; launchOnStartup === CloudConfigDataTypes . ENABLED ? autoLaunchInstance . enableAutoLaunch ( ) : autoLaunchInstance . disableAutoLaunch ( ) ; if ( mainWebContents && ! mainWebContents . isDestroyed ( ) ) { if ( memoryRefresh && memoryRefresh === CloudConfigDataTypes . ENABLED ) { logger . info ( `window-utils: updating the memory threshold` , memoryThreshold ) ; memoryMonitor . setMemoryThreshold ( parseInt ( memoryThreshold , 10 ) ) ; mainWebContents . send ( 'initialize-memory-refresh' ) ; } } } ; param url export const monitorNetworkInterception = ( url ) => { if ( isNetworkMonitorInitialized ) { return ; } const { hostname , protocol } = parse ( url ) ; if ( ! hostname || ! protocol ) { return ; } const mainWebContents = windowHandler . getMainWebContents ( ) ; const podUrl = `${ protocol }//${ hostname }/` ; logger . info ( 'window-utils: monitoring network interception for url' , podUrl ) ; const filter = { urls : [ podUrl + '*' ] } ; if ( mainWebContents && ! mainWebContents . isDestroyed ( ) ) { isNetworkMonitorInitialized = true ; mainWebContents . session . webRequest . onErrorOccurred ( filter , async ( details ) => { if ( ! mainWebContents || mainWebContents . isDestroyed ( ) ) { return ; } if ( ! windowHandler . isMana && windowHandler . isWebPageLoading && ( details . error === 'net::ERR_INTERNET_DISCONNECTED' || details . error === 'net::ERR_NETWORK_CHANGED' || details . error === 'net::ERR_NAME_NOT_RESOLVED' ) ) { logger . error ( `window-utils: URL failed to load` , details ) ; mainWebContents . send ( 'show-banner' , { show : true , bannerType : 'error' , url : podUrl , } ) ; } } ) ; } } ; export const loadBrowserViews = async ( mainWindow ) : Promise < WebContents > => { mainWindow . setMenuBarVisibility ( false ) ; const titleBarView = new BrowserView ( { webPreferences : { sandbox : ! isNodeEnv , nodeIntegration : isNodeEnv , preload : path . join ( __dirname , '../renderer/_preload-component.js' ) , devTools : isDevEnv , } , } ) as ICustomBrowserView ; const mainWindowBounds = windowHandler . getMainWindow ( ) ?. getBounds ( ) ; const mainView = new BrowserView ( { ... windowHandler . getMainWindowOpts ( ) , ... { width : mainWindowBounds ?. width || DEFAULT_WIDTH , height : mainWindowBounds ?. height || DEFAULT_HEIGHT , x : 0 , y : TITLE_BAR_HEIGHT , } , } ) as ICustomBrowserView ; mainWindow . addBrowserView ( titleBarView ) ; mainWindow . addBrowserView ( mainView ) ; const titleBarWindowUrl = format ( { pathname : require . resolve ( '../renderer/react-window.html' ) , protocol : 'file' , query : { componentName : 'title-bar' , locale : i18n . getLocale ( ) , } , slashes : true , } ) ; titleBarView . webContents . once ( 'did-finish-load' , ( ) => { if ( ! titleBarView || titleBarView . webContents . isDestroyed ( ) ) { return ; } titleBarView ?. webContents . send ( 'page-load' , { isWindowsOS , locale : i18n . getLocale ( ) , resource : i18n . loadedResources , isMainWindow : true , } ) ; mainEvents . subscribeMultipleEvents ( TITLE_BAR_EVENTS , titleBarView . webContents ) ; mainWindow ?. on ( 'enter-full-screen' , ( ) => { if ( ! titleBarView || ! viewExists ( titleBarView ) || ! mainWindow || ! windowExists ( mainWindow ) ) { return ; } setTimeout ( ( ) => { const [ width , height ] = mainWindow . getSize ( ) ; titleBarView . setBounds ( { x : 0 , y : 0 , width , height : 0 } ) ; mainWindow . removeBrowserView ( titleBarView ) ; if ( ! mainView || ! viewExists ( mainView ) ) { return ; } mainView . setBounds ( { width , height , x : 0 , y : 0 , } ) ; } , 500 ) ; mainEvents . publish ( 'enter-full-screen' ) ; } ) ; mainWindow ?. on ( 'leave-full-screen' , ( ) => { if ( ! titleBarView || ! viewExists ( titleBarView ) || ! mainWindow || ! windowExists ( mainWindow ) ) { return ; } setTimeout ( ( ) => { const [ width , height ] = mainWindow . getSize ( ) ; mainWindow . addBrowserView ( titleBarView ) ; titleBarView . setBounds ( { x : 0 , y : 0 , width , height : TITLE_BAR_HEIGHT } ) ; if ( ! mainView || ! viewExists ( mainView ) ) { return ; } mainView . setBounds ( { width , height , x : 0 , y : TITLE_BAR_HEIGHT , } ) ; if ( mainView . webContents . isDevToolsOpened ( ) ) { mainView . webContents . toggleDevTools ( ) ; mainView . webContents . toggleDevTools ( ) ; } } , 500 ) ; mainEvents . publish ( 'leave-full-screen' ) ; } ) ; mainWindow ?. on ( 'maximize' , ( ) => { if ( ! mainView || ! viewExists ( mainView ) ) { return ; } const [ width , height ] = mainWindow . getSize ( ) ; mainView . setBounds ( { width , height , x : 0 , y : TITLE_BAR_HEIGHT , } ) ; } ) ; mainWindow ?. on ( 'unmaximize' , ( ) => { if ( ! mainView || ! viewExists ( mainView ) ) { return ; } const [ width , height ] = mainWindow . getSize ( ) ; mainView . setBounds ( { width , height , x : 0 , y : TITLE_BAR_HEIGHT , } ) ; if ( mainView . webContents . isDevToolsOpened ( ) ) { mainView . webContents . toggleDevTools ( ) ; mainView . webContents . toggleDevTools ( ) ; } } ) ; if ( mainWindow ?. isMaximized ( ) ) { mainEvents . publish ( 'maximize' ) ; } if ( mainWindow ?. isFullScreen ( ) ) { mainEvents . publish ( 'enter-full-screen' ) ; } } ) ; await titleBarView . webContents . loadURL ( titleBarWindowUrl ) ; titleBarView . setBounds ( { ... mainWindow . getBounds ( ) , ... { x : 0 , y : 0 , height : TITLE_BAR_HEIGHT } , } ) ; titleBarView . setAutoResize ( { vertical : false , horizontal : true , width : true , height : false , } ) ; mainView . setBounds ( { width : mainWindowBounds ?. width || DEFAULT_WIDTH , height : mainWindowBounds ?. height || DEFAULT_HEIGHT , x : 0 , y : TITLE_BAR_HEIGHT , } ) ; mainView . setAutoResize ( { horizontal : true , vertical : false , width : true , height : false , } ) ; mainWindow . on ( 'resize' , ( ) => { setTimeout ( ( ) => { if ( ! mainView || mainView . webContents . isDestroyed ( ) || ! mainWindow || ! windowExists ( mainWindow ) ) { return ; } const bounds = mainView . getBounds ( ) ; const [ , height ] = mainWindow . getSize ( ) ; mainView . setBounds ( { ... bounds , ... { y : mainWindow . isFullScreen ( ) ? 0 : TITLE_BAR_HEIGHT , height : mainWindow . isFullScreen ( ) ? height : height - TITLE_BAR_HEIGHT , } , } ) ; } , 500 ) ; } ) ; windowHandler . setMainView ( mainView ) ; windowHandler . setTitleBarView ( mainView ) ; return mainView . webContents ; } ;