/** * Core UI functions are initialized in this file. This prevents * unexpected errors from breaking the core features. Specifically, * actions in this file should not require the usage of any internal * modules, excluding dependencies. */ // Requirements const $ = require('jquery') const {ipcRenderer, shell, webFrame} = require('electron') const remote = require('@electron/remote') const isDev = require('./assets/js/isdev') const { LoggerUtil } = require('helios-core') const Lang = require('./assets/js/langloader') const loggerUICore = LoggerUtil.getLogger('UICore') const loggerAutoUpdater = LoggerUtil.getLogger('AutoUpdater') // Log deprecation and process warnings. process.traceProcessWarnings = true process.traceDeprecation = true // Disable eval function. window.eval = global.eval = function () { throw new Error('Sorry, this app does not support window.eval().') } // Display warning when devtools window is opened. remote.getCurrentWebContents().on('devtools-opened', () => { console.log('%cThe console is dark and full of terrors.', 'color: white; -webkit-text-stroke: 4px #a02d2a; font-size: 60px; font-weight: bold') console.log('%cIf you\'ve been told to paste something here, you\'re being scammed.', 'font-size: 16px') console.log('%cUnless you know exactly what you\'re doing, close this window.', 'font-size: 16px') }) // Disable zoom, needed for darwin. webFrame.setZoomLevel(0) webFrame.setVisualZoomLevelLimits(1, 1) const BASE_WINDOW_WIDTH = 1400 const BASE_WINDOW_HEIGHT = 860 const UI_SCALE_MULTIPLIER = 1.12 let responsiveLayoutFrame = null let lastAppliedZoomFactor = null function clamp(value, min, max){ return Math.min(Math.max(value, min), max) } function syncLaunchDetailWidths(){ const launchContent = document.getElementById('launch_content') const launchDetails = document.getElementById('launch_details') const launchButton = document.getElementById('launch_button') const launchProgress = document.getElementById('launch_progress') const launchDetailsRight = document.getElementById('launch_details_right') const launchProgressLabel = document.getElementById('launch_progress_label') if(!launchContent || !launchDetails || !launchButton || !launchProgress || !launchDetailsRight || !launchProgressLabel){ return } const launchContentWidth = launchContent.getBoundingClientRect().width const launchButtonWidth = launchButton.getBoundingClientRect().width const labelWidth = Math.max(64, Math.ceil(launchButtonWidth * 0.48)) const progressWidth = Math.max(220, Math.floor(launchContentWidth - labelWidth - 48)) launchDetails.style.maxWidth = `${Math.ceil(launchContentWidth)}px` launchProgress.style.width = `${progressWidth}px` launchDetailsRight.style.maxWidth = `${progressWidth}px` launchProgressLabel.style.width = `${labelWidth}px` launchProgressLabel.style.minWidth = `${labelWidth}px` launchProgressLabel.style.maxWidth = `${labelWidth}px` } function applyResponsiveLayout(){ const currentWindow = remote.getCurrentWindow() const contentBounds = currentWindow.getContentBounds() const scale = clamp( Math.min(contentBounds.width / BASE_WINDOW_WIDTH, contentBounds.height / BASE_WINDOW_HEIGHT) * UI_SCALE_MULTIPLIER, 0.72, 1.45 ) if(lastAppliedZoomFactor == null || Math.abs(lastAppliedZoomFactor - scale) > 0.001){ lastAppliedZoomFactor = scale webFrame.setZoomFactor(scale) } document.documentElement.style.setProperty('--launcher-scale', scale.toFixed(3)) window.requestAnimationFrame(() => { syncLaunchDetailWidths() }) } function queueResponsiveLayout(){ if(responsiveLayoutFrame != null){ cancelAnimationFrame(responsiveLayoutFrame) } responsiveLayoutFrame = requestAnimationFrame(() => { responsiveLayoutFrame = null applyResponsiveLayout() }) } const responsiveWindow = remote.getCurrentWindow() responsiveWindow.on('resize', queueResponsiveLayout) responsiveWindow.on('maximize', queueResponsiveLayout) responsiveWindow.on('unmaximize', queueResponsiveLayout) // Initialize auto updates in production environments. let updateCheckListener if(!isDev){ ipcRenderer.on('autoUpdateNotification', (event, arg, info) => { switch(arg){ case 'checking-for-update': loggerAutoUpdater.info('Checking for update..') settingsUpdateButtonStatus(Lang.queryJS('uicore.autoUpdate.checkingForUpdateButton'), true) break case 'update-available': loggerAutoUpdater.info('New update available', info.version) if(process.platform === 'darwin'){ info.darwindownload = `https://github.com/peunsu/MRSLauncher/releases/download/v${info.version}/MRS-Launcher-setup-${info.version}${process.arch === 'arm64' ? '-arm64' : '-x64'}.dmg` showUpdateUI(info) } populateSettingsUpdateInformation(info) break case 'update-downloaded': loggerAutoUpdater.info('Update ' + info.version + ' ready to be installed.') settingsUpdateButtonStatus(Lang.queryJS('uicore.autoUpdate.installNowButton'), false, () => { if(!isDev){ ipcRenderer.send('autoUpdateAction', 'installUpdateNow') } }) showUpdateUI(info) break case 'update-not-available': loggerAutoUpdater.info('No new update found.') settingsUpdateButtonStatus(Lang.queryJS('uicore.autoUpdate.checkForUpdatesButton')) break case 'ready': updateCheckListener = setInterval(() => { ipcRenderer.send('autoUpdateAction', 'checkForUpdate') }, 1800000) ipcRenderer.send('autoUpdateAction', 'checkForUpdate') break case 'realerror': if(info != null && info.code != null){ if(info.code === 'ERR_UPDATER_INVALID_RELEASE_FEED'){ loggerAutoUpdater.info('No suitable releases found.') } else if(info.code === 'ERR_XML_MISSED_ELEMENT'){ loggerAutoUpdater.info('No releases found.') } else { loggerAutoUpdater.error('Error during update check..', info) loggerAutoUpdater.debug('Error Code:', info.code) } } break default: loggerAutoUpdater.info('Unknown argument', arg) break } }) } /** * Send a notification to the main process changing the value of * allowPrerelease. If we are running a prerelease version, then * this will always be set to true, regardless of the current value * of val. * * @param {boolean} val The new allow prerelease value. */ function changeAllowPrerelease(val){ ipcRenderer.send('autoUpdateAction', 'allowPrereleaseChange', val) } function showUpdateUI(info){ //TODO Make this message a bit more informative `${info.version}` document.getElementById('image_seal_container').setAttribute('update', true) document.getElementById('image_seal_container').onclick = () => { /*setOverlayContent('Update Available', 'A new update for the launcher is available. Would you like to install now?', 'Install', 'Later') setOverlayHandler(() => { if(!isDev){ ipcRenderer.send('autoUpdateAction', 'installUpdateNow') } else { console.error('Cannot install updates in development environment.') toggleOverlay(false) } }) setDismissHandler(() => { toggleOverlay(false) }) toggleOverlay(true, true)*/ switchView(getCurrentView(), VIEWS.settings, 500, 500, () => { settingsNavItemListener(document.getElementById('settingsNavUpdate'), false) }) } } /* jQuery Example $(function(){ loggerUICore.info('UICore Initialized'); })*/ document.addEventListener('readystatechange', function () { if (document.readyState === 'interactive'){ loggerUICore.info('UICore Initializing..') // Bind close button. Array.from(document.getElementsByClassName('fCb')).map((val) => { val.addEventListener('click', e => { const window = remote.getCurrentWindow() window.close() }) }) // Bind restore down button. Array.from(document.getElementsByClassName('fRb')).map((val) => { val.addEventListener('click', e => { const window = remote.getCurrentWindow() if(window.isMaximized()){ window.unmaximize() } else { window.maximize() } document.activeElement.blur() }) }) // Bind minimize button. Array.from(document.getElementsByClassName('fMb')).map((val) => { val.addEventListener('click', e => { const window = remote.getCurrentWindow() window.minimize() document.activeElement.blur() }) }) // Remove focus from social media buttons once they're clicked. Array.from(document.getElementsByClassName('mediaURL')).map(val => { val.addEventListener('click', e => { document.activeElement.blur() }) }) } else if(document.readyState === 'complete'){ queueResponsiveLayout() setTimeout(() => { queueResponsiveLayout() }, 150) } }, false) /** * Open web links in the user's default browser. */ $(document).on('click', 'a[href^="http"]', function(event) { event.preventDefault() shell.openExternal(this.href) }) /** * Opens DevTools window if you hold (ctrl + shift + i). * This will crash the program if you are using multiple * DevTools, for example the chrome debugger in VS Code. */ document.addEventListener('keydown', function (e) { if((e.key === 'I' || e.key === 'i') && e.ctrlKey && e.shiftKey){ let window = remote.getCurrentWindow() window.toggleDevTools() } })