import Logger from "./utils/Logger" ; import { app , BrowserWindow , dialog , ipcMain , IpcMainEvent , Notification , SaveDialogReturnValue , OpenDialogReturnValue , shell , DownloadItem } from "electron" ; if ( ! app . isPackaged ) { if ( process . env . DDMM_CHINA ) { Logger . debug ( "Localised Mode" , "DDMM_CHINA set, using China constants" ) ; global . ddmm_constants = require ( "./constants/china" ) ; } else { Logger . debug ( "Localised Mode" , "Defaulting to global mode" ) ; global . ddmm_constants = require ( "./constants/global" ) ; } } else { try { global . ddmm_constants = require ( "./constants/china" ) ; } catch ( e ) { global . ddmm_constants = require ( "./constants/global" ) ; } } console . dir ( global . ddmm_constants ) ; import * as Sentry from "@sentry/electron" ; if ( ! global . ddmm_constants . sentry_disabled ) { Sentry . init ( { dsn : "https://bf0edf3f287344d4969e3171c33af4ea@sentry.io/1297252" , onFatalError : ( ) => { } } ) ; } import { copyFileSync , existsSync , mkdirpSync , move , readdirSync , removeSync , unlinkSync } from "fs-extra" ; import { join as joinPath } from "path" ; if ( existsSync ( joinPath ( app . getPath ( "appData" ) , "Doki Doki Mod Manager!" ) ) ) { Logger . info ( "Startup" , "Prehistoric install folder found, using that." ) ; app . setPath ( "userData" , joinPath ( app . getPath ( "appData" ) , "Doki Doki Mod Manager!" ) ) ; } else { app . setPath ( "userData" , joinPath ( app . getPath ( "appData" ) , "DokiDokiModManager" ) ) ; } import DummyDiscordManager from "./discord/DummyDiscordManager" ; import { autoUpdater , UpdateCheckResult } from "electron-updater" ; import { sync as getDataURI } from "datauri" ; import ModList from "./mod/ModList" ; import I18n from "./i18n/i18n" ; import InstallList from "./install/InstallList" ; import InstallLauncher from "./install/InstallLauncher" ; import Config from "./utils/Config" ; import InstallCreator from "./install/InstallCreator" ; import ModInstaller from "./mod/ModInstaller" ; import InstallManager from "./install/InstallManager" ; import DiscordManager from "./discord/DiscordManager" ; import DownloadManager from "./net/DownloadManager" ; import OnboardingManager from "./onboarding/OnboardingManager" ; import { checkSync , DiskUsage } from "diskusage" ; import IntegrityCheck from "./onboarding/IntegrityCheck" ; import { downloadLanguageFile } from "./i18n/TranslationDownload" ; import FeatureFlags from "./utils/FeatureFlags" ; import SpecialCaseManager from "./mod/SpecialCaseManager" ; import IDiscordManager from "./discord/IDiscordManager" ; import BundledUIServer from "./utils/BundledUIServer" ; const DISCORD_ID = "453299645725016074" ; app . userAgentFallback = "DokiDokiModManager/" + app . getVersion ( ) + " (zudo@doki.space)" ; const lastArg = process . argv . pop ( ) ; let appWindow ; let richPresence = global . ddmm_constants . discord_disabled ? new DummyDiscordManager ( ) : new DiscordManager ( process . env . DDMM_DISCORD_ID || DISCORD_ID ) ; richPresence . setIdleStatus ( ) ; const uiURL = require ( "../../ddmm.json" ) . ui ; let featureFlags = new FeatureFlags ( ) ; const specialCaseManager = new SpecialCaseManager ( ) ; let modList ; let downloadManager ; let onboardingManager ; let installLauncher ; mkdirpSync ( joinPath ( app . getPath ( "userData" ) , "language" ) ) ; let lang = new I18n ( ) ; downloadLanguageFile ( Config . readConfigValue ( "language" ) ) . catch ( err => { console . warn ( "Language update failed" , err ) ; } ) ; param error param fatal function showError ( error , fatal ) { appWindow . webContents . send ( "error message" , { fatal , stacktrace : error . stack } ) ; appWindow . setClosable ( true ) ; } param folderName async function launchInstall ( folderName ) { appWindow . webContents . send ( "running cover" , { display : true , folder_path : joinPath ( Config . readConfigValue ( "installFolder" ) , "installs" , folderName ) } ) ; Config . saveConfigValue ( "lastLaunchedInstall" , folderName ) ; appWindow . minimize ( ) ; installLauncher . launchInstall ( folderName , richPresence ) . then ( ( ) => { appWindow . restore ( ) ; appWindow . focus ( ) ; appWindow . webContents . send ( "running cover" , { display : false } ) ; refreshInstalls ( ) ; } ) . catch ( e => { appWindow . restore ( ) ; appWindow . focus ( ) ; appWindow . webContents . send ( "running cover" , { display : false } ) ; showError ( e , false ) ; } ) ; } ipcMain . on ( "restart" , ( ) => { app . relaunch ( ) ; app . quit ( ) ; } ) ; ipcMain . on ( "get modlist" , ( ) => { appWindow . webContents . send ( "got modlist" , modList . getModList ( ) ) ; } ) ; function refreshInstalls ( ) { InstallList . getInstallList ( ) . then ( installs => { appWindow . webContents . send ( "got installs" , installs ) ; } ) ; } ipcMain . on ( "get installs" , ( ) => { refreshInstalls ( ) ; } ) ; ipcMain . on ( "translate" , ( ev , query ) => { let passArgs = query . args ; passArgs . unshift ( query . key ) ; ev . returnValue = lang . translate . apply ( lang , passArgs ) ; } ) ; ipcMain . on ( "open url" , ( ev , url ) => { shell . openExternal ( url ) ; } ) ; ipcMain . on ( "show file" , ( ev , path ) => { shell . showItemInFolder ( path ) ; } ) ; ipcMain . on ( "save config" , ( ev , configData ) => { Config . saveConfigValue ( configData . key , configData . value ) ; } ) ; ipcMain . on ( "read config" , ( ev , key ) => { ev . returnValue = Config . readConfigValue ( key ) ; } ) ; ipcMain . on ( "get feature flag" , ( ev , flag ) => { ev . returnValue = featureFlags . get ( flag ) ; } ) ; ipcMain . on ( "launch install" , ( ev , folderName ) => { launchInstall ( folderName ) ; } ) ; ipcMain . on ( "kill game" , ( ) => { installLauncher . forceKill ( ) ; } ) ; function sendDownloads ( ) { appWindow . webContents . send ( "got downloads" , downloadManager . getActiveDownloads ( ) . map ( ( dl ) => { return { filename : dl . getFilename ( ) , downloaded : dl . getReceivedBytes ( ) , total : dl . getTotalBytes ( ) , startTime : dl . getStartTime ( ) * 1000 } ; } ) ) ; } ipcMain . on ( "get downloads" , ( ) => { sendDownloads ( ) ; } ) ; ipcMain . on ( "start download" , ( ev , data ) => { if ( data . interaction ) { downloadManager . downloadFileWithInteraction ( data . url ) . then ( ( ) => { appWindow . webContents . send ( "download started" ) ; } ) ; } else { downloadManager . downloadFile ( data . url ) ; } } ) ; ipcMain . on ( "preload download name" , ( ev , name ) => { downloadManager . preloadFilename ( name ) ; } ) ; ipcMain . on ( "browse mods" , ( ev ) => { const extensions = [ "zip" , "7z" , "rar" , "rpa" ] ; dialog . showOpenDialog ( appWindow , { title : lang . translate ( "main.mod_browse_dialog.title" ) , filters : [ { extensions : extensions , name : lang . translate ( "main.mod_browse_dialog.file_format_name" ) } ] , } ) . then ( ( res ) => { if ( res . filePaths && res . filePaths [ 0 ] && extensions . find ( ext => ( res . filePaths [ 0 ] . endsWith ( "." + ext ) ) ) ) { ev . returnValue = res . filePaths [ 0 ] ; } else { ev . returnValue = null ; } } ) ; } ) ; ipcMain . on ( "select folder" , ( ev ) => { dialog . showOpenDialog ( appWindow , { title : lang . translate ( "main.move_install.title" ) , properties : [ "openDirectory" ] } ) . then ( ( res ) => { if ( res . filePaths && res . filePaths [ 0 ] ) { let error = null ; try { const files = readdirSync ( res . filePaths [ 0 ] ) ; if ( files . length > 0 ) { if ( ! ( files . length <= 4 && files . indexOf ( "installs" ) !== - 1 && files . indexOf ( "mods" ) !== - 1 ) ) { error = lang . translate ( "main.move_install.error_not_empty" ) ; } } } catch { error = lang . translate ( "main.move_install.error_read" ) ; } finally { ev . returnValue = { error , path : res . filePaths [ 0 ] } ; } } else { ev . returnValue = null ; } } ) ; } ) ; ipcMain . on ( "create install" , ( ev , install ) => { appWindow . setClosable ( false ) ; Logger . info ( "IPC" , "Creating install in folder " + install . folderName ) ; InstallCreator . createInstall ( install . folderName , install . installName , install . globalSave , featureFlags . get ( "hookInjector" ) ) . then ( ( ) => { if ( ! install . mod ) { refreshInstalls ( ) ; appWindow . setClosable ( true ) ; } else { Logger . info ( "IPC" , "Installing mod " + install . mod + " in " + install . folderName ) ; ModInstaller . installMod ( install . mod , joinPath ( Config . readConfigValue ( "installFolder" ) , "installs" , install . folderName , "install" ) , specialCaseManager ) . then ( ( ) => { refreshInstalls ( ) ; appWindow . setClosable ( true ) ; } ) . catch ( ( e ) => { refreshInstalls ( ) ; showError ( e , false ) ; } ) ; } } ) . catch ( ( e ) => { refreshInstalls ( ) ; showError ( e , false ) ; } ) ; } ) ; ipcMain . on ( "unarchive install" , ( ev , install ) => { appWindow . setClosable ( false ) ; InstallCreator . createInstall ( install . folderName ) . then ( ( ) => { if ( ! install . mod ) { refreshInstalls ( ) ; appWindow . setClosable ( true ) ; launchInstall ( install . folderName ) ; } else { ModInstaller . installMod ( install . mod , joinPath ( Config . readConfigValue ( "installFolder" ) , "installs" , install . folderName , "install" ) , specialCaseManager ) . then ( ( ) => { refreshInstalls ( ) ; appWindow . setClosable ( true ) ; launchInstall ( install . folderName ) ; } ) . catch ( ( e ) => { refreshInstalls ( ) ; showError ( e , false ) ; } ) ; } } ) . catch ( ( e ) => { refreshInstalls ( ) ; showError ( e , false ) ; } ) ; } ) ; ipcMain . on ( "rename install" , ( ev , options ) => { Logger . info ( "IPC" , "Renaming install in " + options . folderName + " to " + options . newName ) ; InstallManager . renameInstall ( options . folderName , options . newName ) . then ( ( ) => { refreshInstalls ( ) ; } ) . catch ( ( e ) => { showError ( e , false ) ; } ) ; } ) ; ipcMain . on ( "archive install" , ( ev , folderName ) => { InstallManager . archiveInstall ( folderName ) . then ( ( ) => { refreshInstalls ( ) ; } ) ; } ) ; ipcMain . on ( "delete install" , ( ev , folderName ) => { Logger . info ( "IPC" , "Deleting install in " + folderName ) ; InstallManager . deleteInstall ( folderName ) . then ( ( ) => { refreshInstalls ( ) ; } ) . catch ( ( e ) => { showError ( e , false ) ; } ) ; } ) ; ipcMain . on ( "delete mod" , ( ev , fileName ) => { Logger . info ( "IPC" , "Deleting mod " + fileName ) ; try { unlinkSync ( joinPath ( Config . readConfigValue ( "installFolder" ) , "mods" , fileName ) ) ; appWindow . webContents . send ( "got modlist" , modList . getModList ( ) ) ; } catch ( e ) { showError ( e , false ) ; } } ) ; ipcMain . on ( "delete save" , ( ev , folderName ) => { Logger . info ( "IPC" , "Deleting save data for " + folderName ) ; InstallManager . deleteSaveData ( folderName ) . then ( ( ) => { refreshInstalls ( ) ; } ) . catch ( ( e ) => { showError ( e , false ) ; } ) ; } ) ; ipcMain . on ( "set category" , ( ev , options ) => { Logger . info ( "IPC" , "Setting category of " + options . folderName + " to " + options . category ) ; InstallManager . setCategory ( options . folderName , options . category ) . then ( ( ) => { refreshInstalls ( ) ; } ) . catch ( ( e ) => { showError ( e , false ) ; } ) ; } ) ; ipcMain . on ( "create shortcut" , ( ev , options ) => { if ( process . platform !== "win32" ) { dialog . showErrorBox ( "Shortcut creation is only supported on Windows" , "Nice try." ) ; return ; } dialog . showSaveDialog ( appWindow , { title : lang . translate ( "main.shortcut_dialog.title" ) , defaultPath : options . installName , filters : [ { name : lang . translate ( "main.shortcut_dialog.file_format_name" ) , extensions : [ "lnk" ] } ] } ) . then ( ( dialogReturn ) => { const file = dialogReturn . filePath ; if ( file ) { Logger . info ( "IPC" , "Creating shortcut to " + options . folderName ) ; if ( ! shell . writeShortcutLink ( file , "create" , { target : "ddmm://launch-install/" + options . folderName , icon : process . execPath , iconIndex : 0 } ) ) { showError ( new Error ( "Attempt to write shortcut failed" ) , false ) ; } } } ) ; } ) ; ipcMain . on ( "get install background" , ( ev , folder ) => { const installFolder = joinPath ( Config . readConfigValue ( "installFolder" ) , "installs" ) ; let bgPath = joinPath ( installFolder , folder , "ddmm-bg.png" ) ; let bgDataURL ; let screenshots ; try { screenshots = readdirSync ( joinPath ( installFolder , folder , "install" ) ) . filter ( fn => { return fn . match ( /^screenshot(\d+)\.png$/ ) ; } ) ; } catch ( e ) { screenshots = [ ] ; } if ( existsSync ( bgPath ) ) { bgDataURL = getDataURI ( bgPath ) ; } else if ( screenshots . length > 0 ) { bgDataURL = getDataURI ( joinPath ( installFolder , folder , "install" , screenshots [ Math . floor ( Math . random ( ) * screenshots . length ) ] ) ) ; } ev . returnValue = bgDataURL ; } ) ; ipcMain . on ( "install exists" , ( ev , folderName ) => { if ( ! folderName || typeof folderName !== "string" ) { ev . returnValue = false ; return ; } ev . returnValue = InstallManager . installExists ( folderName ) ; } ) ; ipcMain . on ( "move install" , ( ev , newInstallFolder ) => { Logger . info ( "Install Move" , "Moving install directory to " + newInstallFolder ) ; appWindow . hide ( ) ; const oldInstallFolder = Config . readConfigValue ( "installFolder" ) ; move ( oldInstallFolder , newInstallFolder , { overwrite : true } , e => { if ( e ) { Logger . warn ( "Install Move" , "Failed to move install directory: " + e . message ) ; dialog . showErrorBox ( lang . translate ( "main.errors.move_install.title" ) , lang . translate ( "main.errors.move_install.body" ) ) ; } else { Config . saveConfigValue ( "installFolder" , newInstallFolder ) ; } app . relaunch ( ) ; app . quit ( ) ; } ) ; } ) ; ipcMain . on ( "get backgrounds" , ( ev ) => { ev . returnValue = readdirSync ( joinPath ( __dirname , "../../src/renderer/images/backgrounds" ) ) ; } ) ; ipcMain . on ( "get constants" , ( ev ) => { ev . returnValue = global . ddmm_constants ; } ) ; ipcMain . on ( "debug crash" , ( ) => { throw new Error ( "User forced debug crash with DevTools" ) ; } ) ; ipcMain . on ( "disk space" , ( ev , path ) => { try { const usage = checkSync ( path ) ; Logger . debug ( "Disk Usage" , "Remaining space in " + path + " is " + usage . free ) ; ev . returnValue = usage . free ; } catch ( e ) { Logger . warn ( "Disk Usage" , "Unable to read disk usage from " + path ) ; ev . returnValue = 999999 ; } } ) ; ipcMain . on ( "import mas" , ( ev , folderName ) => { dialog . showOpenDialog ( appWindow , { title : lang . translate ( "main.import_mas.title" ) , filters : [ { name : lang . translate ( "main.import_mas.filter" ) , extensions : [ "*" ] } ] } ) . then ( ( res ) => { if ( res . filePaths && res . filePaths [ 0 ] ) { copyFileSync ( res . filePaths [ 0 ] , joinPath ( Config . readConfigValue ( "installFolder" ) , "installs" , folderName , "install" , "characters" , "monika" ) ) ; removeSync ( res . filePaths [ 0 ] ) ; InstallManager . setMonikaExported ( folderName , false ) . then ( ( ) => { refreshInstalls ( ) ; } ) ; } } ) ; } ) ; ipcMain . on ( "export mas" , ( ev , folderName ) => { dialog . showSaveDialog ( appWindow , { title : lang . translate ( "main.export_mas.title" ) , defaultPath : "monika" , filters : [ { name : lang . translate ( "main.export_mas.filter" ) , extensions : [ "*" ] } ] } ) . then ( ( dialogReturn ) => { if ( dialogReturn . filePath ) { Logger . info ( "MAS" , "Exporting Monika from " + folderName ) ; const source = joinPath ( Config . readConfigValue ( "installFolder" ) , "installs" , folderName , "install" , "characters" , "monika" ) ; copyFileSync ( source , dialogReturn . filePath ) ; removeSync ( source ) ; InstallManager . setMonikaExported ( folderName , true ) . then ( ( ) => { } ) ; } } ) ; } ) ; ipcMain . on ( "reload languages" , ( ) => { downloadLanguageFile ( Config . readConfigValue ( "language" ) ) . catch ( err => { console . warn ( "Language update failed" , err ) ; } ) . finally ( ( ) => { lang = new I18n ( ) ; appWindow . webContents . send ( "languages reloaded" ) ; } ) ; } ) ; ipcMain . on ( "onboarding scan" , ( ) => { const guessPath = joinPath ( app . getPath ( "downloads" ) , process . platform === "darwin" ? "ddlc-mac.zip" : "ddlc-win.zip" ) ; Logger . info ( "Onboarding" , "Testing " + guessPath ) ; if ( existsSync ( guessPath ) ) { Logger . info ( "Onboarding" , "Download guess exists, checking..." ) ; IntegrityCheck . checkGameIntegrity ( guessPath ) . then ( version => { Logger . info ( "Onboarding" , "Game found automatically!" ) ; appWindow . webContents . send ( "onboarding validated" , { path : guessPath , success : true , version_match : ( process . platform === "darwin" ? version === "mac" : version == "windows" ) } ) ; } ) ; } else { appWindow . webContents . send ( "onboarding validated" , { path : guessPath , success : false , version_match : false } ) ; } } ) ; ipcMain . on ( "onboarding browse" , ( ev ) => { dialog . showOpenDialog ( appWindow , { title : lang . translate ( "main.game_browse_dialog.title" ) , filters : [ { name : lang . translate ( "main.game_browse_dialog.file_format_name" ) , extensions : [ "zip" ] } ] } ) . then ( ( res ) => { if ( res . filePaths && res . filePaths [ 0 ] ) { ev . returnValue = res . filePaths [ 0 ] ; } else { ev . returnValue = null ; } } ) ; } ) ; ipcMain . on ( "onboarding validate" , ( ev , path ) => { IntegrityCheck . checkGameIntegrity ( path ) . then ( version => { appWindow . webContents . send ( "onboarding validated" , { path , success : true , version_match : ( process . platform === "darwin" ? version === "mac" : version == "windows" ) } ) ; } ) . catch ( ( ) => { appWindow . webContents . send ( "onboarding validated" , { path , success : false , version_match : false } ) ; } ) ; } ) ; ipcMain . on ( "onboarding finalise" , ( ev , ddlcPath ) => { Logger . info ( "Onboarding" , "Creating directory structure" ) ; mkdirpSync ( joinPath ( Config . readConfigValue ( "installFolder" ) , "mods" ) ) ; mkdirpSync ( joinPath ( Config . readConfigValue ( "installFolder" ) , "installs" ) ) ; mkdirpSync ( joinPath ( Config . readConfigValue ( "installFolder" ) , "downloads" ) ) ; copyFileSync ( ddlcPath , joinPath ( Config . readConfigValue ( "installFolder" ) , "ddlc.zip" ) ) ; } ) ; ipcMain . on ( "download mod" , ( ev , url ) => { downloadManager . downloadFile ( url ) ; } ) ; autoUpdater . autoDownload = false ; autoUpdater . on ( "download-progress" , ( ) => { appWindow . webContents . send ( "update status" , "downloading" ) ; } ) ; autoUpdater . on ( "update-downloaded" , ( ) => { appWindow . webContents . send ( "update status" , "downloaded" ) ; } ) ; ipcMain . on ( "download update" , ( ) => { autoUpdater . checkForUpdates ( ) . then ( ( update ) => { if ( update ) { autoUpdater . downloadUpdate ( ) ; } } ) ; } ) ; process . on ( "uncaughtException" , ( e ) => { Logger . error ( "Application" , "An uncaught exception occurred in the main process" ) ; console . error ( e ) ; showError ( e , true ) ; } ) ; function handleURL ( forcedArg ? ) { const url = forcedArg ? forcedArg : lastArg ; if ( url . startsWith ( "ddmm://" ) ) { const commandParts = url . split ( "ddmm://" ) [ 1 ] . split ( "/" ) ; const command = commandParts . shift ( ) ; if ( command === "launch-install" ) { const installFolder = commandParts [ 0 ] ; launchInstall ( installFolder ) ; } else if ( command === "download-mod" ) { const data = JSON . parse ( Buffer . from ( commandParts . join ( "/" ) , "base64" ) . toString ( ) ) ; if ( data . filename && data . url ) { const oldFilename = downloadManager . getPreloadedFilename ( ) ; downloadManager . preloadFilename ( data . filename ) ; downloadManager . downloadFileWithInteraction ( data . url ) . then ( ( ) => { downloadManager . preloadFilename ( oldFilename ) ; } ) ; } } } } app . on ( "second-instance" , ( ev , argv ) => { appWindow . restore ( ) ; appWindow . focus ( ) ; handleURL ( argv . pop ( ) ) ; } ) ; app . on ( "ready" , ( ) => { if ( ! app . requestSingleInstanceLock ( ) ) { Logger . info ( "Startup" , "Another instance is running, quitting." ) ; app . quit ( ) ; return ; } if ( app . isPackaged ) { app . setAsDefaultProtocolClient ( "ddmm" ) ; } appWindow = new BrowserWindow ( { width : 1024 , height : 600 , minWidth : 1000 , minHeight : 600 , maximizable : true , frame : ! ! Config . readConfigValue ( "systemBorders" ) , useContentSize : true , webPreferences : { contextIsolation : false , sandbox : false , nodeIntegration : false , nativeWindowOpen : true , webviewTag : true , preload : joinPath ( __dirname , "../../src/renderer/js-preload/preload.js" ) } , titleBarStyle : "hiddenInset" , show : false , title : "Doki Doki Mod Manager" + ( process . env . DDMM_DEVELOPER ? " - Development Mode" : "" ) } ) ; appWindow . on ( "page-title-updated" , event => { event . preventDefault ( ) ; } ) ; downloadManager = new DownloadManager ( appWindow , lang ) ; onboardingManager = new OnboardingManager ( ) ; modList = new ModList ( downloadManager ) ; installLauncher = new InstallLauncher ( ) ; downloadManager . on ( "started" , ( ) => { appWindow . webContents . send ( "download started" ) ; } ) ; downloadManager . on ( "updated" , ( ) => { sendDownloads ( ) ; } ) ; downloadManager . on ( "finished" , ( ) => { appWindow . webContents . send ( "got modlist" , modList . getModList ( ) ) ; } ) ; appWindow . webContents . on ( "will-navigate" , ( ev , url ) => { console . warn ( "Prevented navigation from app container" , url ) ; ev . preventDefault ( ) ; } ) ; appWindow . on ( "ready-to-show" , ( ) => { appWindow . show ( ) ; } ) ; appWindow . webContents . on ( "crashed" , ( ) => { const crashNotif = new Notification ( { title : lang . translate ( "main.errors.exception.title" ) , body : lang . translate ( "main.errors.exception.body" ) , } ) ; crashNotif . show ( ) ; app . quit ( ) ; } ) ; appWindow . on ( "unresponsive" , ( ) => { const freezeNotif = new Notification ( { title : lang . translate ( "main.errors.renderer_freeze.title" ) , body : lang . translate ( "main.errors.renderer_freeze.body" ) , } ) ; freezeNotif . show ( ) ; } ) ; appWindow . on ( "close" , ( ev ) => { if ( downloadManager . hasActiveDownloads ( ) ) { ev . preventDefault ( ) ; } } ) ; appWindow . on ( "closed" , ( ) => { appWindow = null ; app . quit ( ) ; } ) ; appWindow . webContents . on ( "did-finish-load" , ( ) => { OnboardingManager . requiresOnboarding ( ) . catch ( e => { Logger . info ( "Onboarding" , "Onboarding required - reason: " + e ) ; appWindow . webContents . send ( "start onboarding" ) ; } ) ; } ) ; appWindow . webContents . once ( "did-finish-load" , ( ) => { handleURL ( ) ; } ) ; if ( ! process . env . DDMM_DEVELOPER ) { appWindow . setMenu ( null ) ; appWindow . setMenuBarVisibility ( false ) ; } appWindow . webContents . once ( "did-fail-load" , ( ) => { if ( Config . readConfigValue ( "localUI" ) ) { dialog . showErrorBox ( lang . translate ( "main.errors.dev_ui_load_fail.title" ) , lang . translate ( "main.errors.dev_ui_load_fail.body" ) ) ; appWindow . loadURL ( uiURL ) ; } else { Logger . warn ( "UI" , "Displaying offline message." ) ; if ( global . ddmm_constants . force_bundled_ui ) { appWindow . loadFile ( joinPath ( __dirname , "../../src/renderer/html/offline.html" ) ) ; } else { BundledUIServer . start ( ) . then ( port => { appWindow . loadURL ( `http://localhost:${ port }/` ) ; } ) ; } } } ) ; if ( Config . readConfigValue ( "localUI" ) ) { appWindow . loadURL ( Config . readConfigValue ( "localUI" ) ) ; } else { if ( global . ddmm_constants . force_bundled_ui ) { BundledUIServer . start ( ) . then ( port => { appWindow . loadURL ( `http://localhost:${ port }/` ) ; } ) ; } else { appWindow . loadURL ( uiURL ) ; } } if ( ! Config . readConfigValue ( "installFolder" , true ) ) { Config . saveConfigValue ( "installFolder" , Config . readConfigValue ( "installFolder" ) ) ; } Logger . info ( "Startup" , "Application startup complete!" ) ; } ) ;