import { BrowserWindow , shell , dialog , Event , BrowserWindowConstructorOptions , WebContents , nativeTheme } from 'electron' ; import windowStateKeeper from 'electron-window-state' ; import isDev from 'electron-is-dev' ; import { isOSX , nativeTabsSupported , onNavigation , onNewWindowHelper } from './helpers' ; import { initContextMenu } from './context-menu' ; import { start as osHelperStart , views } from './os-service-helper' ; import { start as settingsHelperStart } from './setting-service-helper' ; import { start as terminalServiceStart } from './terminal-service' ; declare const LANDSCAPE_PRELOAD_WEBPACK_ENTRY ; const ZOOM_INTERVAL = 0.1 ; function getWindowOrViewContents ( focusedWindow ) { const view = focusedWindow . getBrowserView ( ) ; return view ? view . webContents : focusedWindow . webContents ; } export function updateZoomLevels ( mainWindow ) { if ( ! isDev ) { return ; } const main = Math . trunc ( mainWindow . webContents . zoomFactor * 10 ) / 10 ; const viewLevels = [ ] ; views . forEach ( view => { viewLevels . push ( Math . trunc ( view . webContents . zoomFactor * 10 ) / 10 ) ; } ) ; mainWindow . webContents . send ( 'zoom-levels' , { main , views : viewLevels . join ( ) } ) ; } function adjustZoom ( mainWindow , adjuster ) { const focusedWindow = BrowserWindow . getFocusedWindow ( ) ; const view = focusedWindow . getBrowserView ( ) ; if ( focusedWindow === mainWindow && view ) { adjuster ( view . webContents ) ; } adjuster ( focusedWindow . webContents ) ; } export function createMainWindow ( mainUrl , socketName , onAppQuit , bgWindow ? ) { const mainWindowState = windowStateKeeper ( { defaultWidth : 1280 , defaultHeight : 800 , } ) ; const DEFAULT_WINDOW_OPTIONS = { title : 'Port' , webPreferences : { javascript : true , plugins : true , nodeIntegration : false , webSecurity : true , zoomFactor : 1 , contextIsolation : false , preload : LANDSCAPE_PRELOAD_WEBPACK_ENTRY } , } ; const mainWindow = new BrowserWindow ( { ... DEFAULT_WINDOW_OPTIONS , width : mainWindowState . width , height : mainWindowState . height , titleBarStyle : 'hidden' , backgroundColor : nativeTheme . shouldUseDarkColors ? '#000000' : '#FFFFFF' , webPreferences : { nodeIntegration : true , enableRemoteModule : true } } ) ; mainWindowState . manage ( mainWindow ) ; const withFocusedView = < T > ( block , target = 'view' ) : T | undefined => { const focusedWindow = BrowserWindow . getFocusedWindow ( ) ; if ( focusedWindow ) { const windowContents = focusedWindow . webContents ; const windowOrViewContents = getWindowOrViewContents ( focusedWindow ) ; return target === 'window' ? block ( windowContents ) : block ( windowOrViewContents ) ; } return undefined ; } ; const onZoomIn = ( ) : void => { adjustZoom ( mainWindow , ( contents ) => { contents . zoomFactor += ZOOM_INTERVAL ; } ) ; updateZoomLevels ( mainWindow ) ; } ; const onZoomOut = ( ) : void => { adjustZoom ( mainWindow , ( contents ) => { contents . zoomFactor += - ZOOM_INTERVAL ; } ) ; updateZoomLevels ( mainWindow ) ; } ; const onZoomReset = ( ) : void => { adjustZoom ( mainWindow , ( contents ) => { contents . zoomFactor = 1 ; } ) ; updateZoomLevels ( mainWindow ) ; } ; const clearAppData = async ( ) : Promise < void > => { const response = await dialog . showMessageBox ( mainWindow , { type : 'warning' , buttons : [ 'Yes' , 'Cancel' ] , defaultId : 1 , title : 'Clear cache confirmation' , message : 'This will clear all data (cookies, local storage etc) from this app. Are you sure you wish to proceed?' , } ) ; if ( response . response !== 0 ) { return ; } await clearCache ( mainWindow ) ; } ; const onGoBack = ( ) : void => { withFocusedView ( ( contents ) => { contents . goBack ( ) ; } ) ; } ; const onGoForward = ( ) : void => { withFocusedView ( ( contents ) => { contents . goForward ( ) ; } ) ; } ; const getCurrentUrl = ( ) : string => withFocusedView ( ( contents ) => contents . getURL ( ) ) ; const onBlockedExternalUrl = ( url ) => { dialog . showMessageBox ( mainWindow , { message : `Cannot navigate to external URL: ${ url }` , type : 'error' , title : 'Navigation blocked' , } ) ; } ; const onWillNavigate = ( event , webContents , urlTarget ) : void => { isDev && console . log ( 'will-navigate' , urlTarget ) ; onNavigation ( { preventDefault : event . preventDefault , currentUrl : webContents . getURL ( ) , urlTarget , createNewWindow , mainWindow } ) ; } ; const createNewWindow = ( url ) => { const window = new BrowserWindow ( DEFAULT_WINDOW_OPTIONS ) ; window . webContents . on ( 'new-window' , onNewWindow ( url ) ) ; window . webContents . on ( 'will-navigate' , ( e , url ) => onWillNavigate ( e , window . webContents , url ) ) ; window . webContents . on ( 'did-finish-load' , ( ) => { console . log ( 'finished load' ) ; try { const view = mainWindow . getBrowserView ( ) ; const currentTitle = window . webContents . getTitle ( ) ; const title = `${ currentTitle } | ${ view . webContents . getTitle ( ) }` ; console . log ( 'setting title to' , title ) ; window . setTitle ( title ) ; } catch { console . error ( 'no view' ) ; } } ) ; window . webContents . loadURL ( url ) ; return window ; } ; const createAboutBlankWindow = ( ) : BrowserWindow => { const window = createNewWindow ( 'about:blank' ) ; window . hide ( ) ; window . webContents . once ( 'did-stop-loading' , ( ) => { if ( window . webContents . getURL ( ) === 'about:blank' ) { window . close ( ) ; } else { window . show ( ) ; } } ) ; return window ; } ; const onNewWindow = ( targetUrl ) => ( event , urlToGo , frameName , disposition ) : void => { isDev && console . log ( 'creating new window' , targetUrl , urlToGo , frameName , disposition ) ; const preventDefault = ( newGuest ) : void => { event . preventDefault ( ) ; if ( newGuest ) { event . newGuest = newGuest ; } } ; onNewWindowHelper ( urlToGo , disposition , targetUrl , preventDefault , shell . openExternal . bind ( this ) , createAboutBlankWindow , createNewWindow , false , onBlockedExternalUrl , mainWindow ) ; } ; const menuOptions = { appQuit : onAppQuit , zoomIn : onZoomIn , zoomOut : onZoomOut , zoomReset : onZoomReset , zoomBuildTimeValue : 1.0 , goBack : onGoBack , goForward : onGoForward , getCurrentUrl , clearAppData , mainWindow , bgWindow } ; initContextMenu ( createNewWindow , undefined , mainUrl ) ; mainWindow . webContents . on ( 'new-window' , onNewWindow ( mainUrl ) ) ; mainWindow . webContents . on ( 'will-navigate' , ( e , url ) => onWillNavigate ( e , mainWindow . webContents , url ) ) ; mainWindow . webContents . on ( 'did-start-loading' , ( ) => { const loadingUrl = mainWindow . webContents . getURL ( ) . split ( '#' ) [ 0 ] ; if ( mainUrl === loadingUrl ) { const view = mainWindow . getBrowserView ( ) ; if ( view ) { view . webContents . reload ( ) ; } } } ) ; mainWindow . webContents . on ( 'did-finish-load' , ( ) => { mainWindow . webContents . setVisualZoomLevelLimits ( 1 , 3 ) ; } ) ; mainWindow . webContents . on ( 'dom-ready' , ( ) => { mainWindow . webContents . send ( 'set-socket' , { name : socketName } ) ; } ) ; osHelperStart ( mainWindow , createNewWindow , onNewWindow , bgWindow ) ; settingsHelperStart ( mainWindow , menuOptions ) ; terminalServiceStart ( ) ; isDev && mainWindow . webContents . openDevTools ( ) ; mainWindow . loadURL ( mainUrl ) ; mainWindow . on ( 'close' , ( event ) => { if ( mainWindow . isFullScreen ( ) ) { if ( nativeTabsSupported ( ) ) { mainWindow . moveTabToNewWindow ( ) ; } mainWindow . setFullScreen ( false ) ; mainWindow . once ( 'leave-full-screen' , hideOrCloseWindow . bind ( this , mainWindow , bgWindow , event ) ) ; } hideOrCloseWindow ( mainWindow , bgWindow , event ) ; } ) ; return mainWindow ; } function hideOrCloseWindow ( window , bgWindow , event ) { if ( isOSX ( ) ) { event . preventDefault ( ) ; window . hide ( ) ; } else { bgWindow . destroy ( ) ; isDev && console . log ( 'closing bg and main, and everything else' ) ; } } async function clearCache ( browserWindow ) { const { session } = browserWindow . webContents ; await session . clearStorageData ( ) ; await session . clearCache ( ) ; }