2018-04-25 17:11:10 -07:00
/ * *
* Initialize UI functions which depend on internal modules .
* Loaded after core UI functions are initialized in uicore . js .
* /
// Requirements
const path = require ( 'path' )
2023-02-24 18:02:18 -08:00
const { Type } = require ( 'helios-distribution-types' )
2018-07-22 08:40:15 -07:00
const AuthManager = require ( './assets/js/authmanager' )
const ConfigManager = require ( './assets/js/configmanager' )
2023-02-24 18:02:18 -08:00
const { DistroAPI } = require ( './assets/js/distromanager' )
2019-04-07 20:33:40 -07:00
const Lang = require ( './assets/js/langloader' )
2018-04-25 17:11:10 -07:00
2018-05-06 22:34:57 -07:00
let rscShouldLoad = false
2018-05-08 03:34:16 -07:00
let fatalStartupError = false
2018-05-09 19:23:37 -07:00
// Mapping of each view to their container IDs.
const VIEWS = {
2018-05-29 18:47:55 -07:00
landing : '#landingContainer' ,
2022-02-11 16:51:28 -08:00
loginOptions : '#loginOptionsContainer' ,
2018-05-29 18:47:55 -07:00
login : '#loginContainer' ,
settings : '#settingsContainer' ,
2022-02-11 16:51:28 -08:00
welcome : '#welcomeContainer' ,
waiting : '#waitingContainer'
2018-05-09 19:23:37 -07:00
}
// The currently shown view container.
2018-06-21 17:54:07 -07:00
let currentView
2018-05-09 19:23:37 -07:00
/ * *
* Switch launcher views .
*
* @ param { string } current The ID of the current view container .
* @ param { * } next The ID of the next view container .
* @ param { * } currentFadeTime Optional . The fade out time for the current view .
* @ param { * } nextFadeTime Optional . The fade in time for the next view .
* @ param { * } onCurrentFade Optional . Callback function to execute when the current
* view fades out .
* @ param { * } onNextFade Optional . Callback function to execute when the next view
* fades in .
* /
function switchView ( current , next , currentFadeTime = 500 , nextFadeTime = 500 , onCurrentFade = ( ) => { } , onNextFade = ( ) => { } ) {
2018-05-29 18:47:55 -07:00
currentView = next
2023-02-24 18:02:18 -08:00
$ ( ` ${ current } ` ) . fadeOut ( currentFadeTime , async ( ) => {
await onCurrentFade ( )
$ ( ` ${ next } ` ) . fadeIn ( nextFadeTime , async ( ) => {
await onNextFade ( )
2018-05-09 19:23:37 -07:00
} )
} )
}
/ * *
* Get the currently shown view container .
*
* @ returns { string } The currently shown view container .
* /
function getCurrentView ( ) {
return currentView
}
2023-02-24 18:02:18 -08:00
async function showMainUI ( data ) {
2018-06-04 17:06:34 -07:00
if ( ! isDev ) {
2022-12-24 19:17:30 -08:00
loggerAutoUpdater . info ( 'Initializing..' )
2018-06-04 17:06:34 -07:00
ipcRenderer . send ( 'autoUpdateAction' , 'initAutoUpdater' , ConfigManager . getAllowPrerelease ( ) )
}
2023-02-24 18:02:18 -08:00
await prepareSettings ( true )
updateSelectedServer ( data . getServerById ( ConfigManager . getSelectedServer ( ) ) )
2018-05-08 03:34:16 -07:00
refreshServerStatus ( )
setTimeout ( ( ) => {
2018-06-21 17:54:07 -07:00
document . getElementById ( 'frameBar' ) . style . backgroundColor = 'rgba(0, 0, 0, 0.5)'
2018-05-08 03:34:16 -07:00
document . body . style . backgroundImage = ` url('assets/images/backgrounds/ ${ document . body . getAttribute ( 'bkid' ) } .jpg') `
$ ( '#main' ) . show ( )
2018-05-30 20:32:51 -07:00
const isLoggedIn = Object . keys ( ConfigManager . getAuthAccounts ( ) ) . length > 0
2018-05-29 18:47:55 -07:00
// If this is enabled in a development environment we'll get ratelimited.
// The relaunch frequency is usually far too high.
2018-05-30 20:32:51 -07:00
if ( ! isDev && isLoggedIn ) {
2018-08-22 11:21:49 -07:00
validateSelectedAccount ( )
2018-05-29 18:47:55 -07:00
}
2018-05-09 19:23:37 -07:00
2018-05-08 03:34:16 -07:00
if ( ConfigManager . isFirstLaunch ( ) ) {
2018-06-21 17:54:07 -07:00
currentView = VIEWS . welcome
2018-05-29 18:47:55 -07:00
$ ( VIEWS . welcome ) . fadeIn ( 1000 )
2018-05-08 03:34:16 -07:00
} else {
2018-05-30 20:32:51 -07:00
if ( isLoggedIn ) {
2018-06-21 17:54:07 -07:00
currentView = VIEWS . landing
2018-05-30 20:32:51 -07:00
$ ( VIEWS . landing ) . fadeIn ( 1000 )
} else {
2022-02-11 16:51:28 -08:00
loginOptionsCancelEnabled ( false )
loginOptionsViewOnLoginSuccess = VIEWS . landing
loginOptionsViewOnLoginCancel = VIEWS . loginOptions
currentView = VIEWS . loginOptions
$ ( VIEWS . loginOptions ) . fadeIn ( 1000 )
2018-05-30 20:32:51 -07:00
}
2018-05-08 03:34:16 -07:00
}
setTimeout ( ( ) => {
2018-05-09 19:23:37 -07:00
$ ( '#loadingContainer' ) . fadeOut ( 500 , ( ) => {
2018-05-08 03:34:16 -07:00
$ ( '#loadSpinnerImage' ) . removeClass ( 'rotating' )
} )
2018-05-09 21:01:38 -07:00
} , 250 )
2018-05-08 03:34:16 -07:00
} , 750 )
2018-05-10 02:48:55 -07:00
// Disable tabbing to the news container.
initNews ( ) . then ( ( ) => {
2018-07-22 10:31:15 -07:00
$ ( '#newsContainer *' ) . attr ( 'tabindex' , '-1' )
2018-05-10 02:48:55 -07:00
} )
2018-05-08 03:34:16 -07:00
}
function showFatalStartupError ( ) {
setTimeout ( ( ) => {
$ ( '#loadingContainer' ) . fadeOut ( 250 , ( ) => {
document . getElementById ( 'overlayContainer' ) . style . background = 'none'
setOverlayContent (
'Fatal Error: Unable to Load Distribution Index' ,
'A connection could not be established to our servers to download the distribution index. No local copies were available to load. <br><br>The distribution index is an essential file which provides the latest server information. The launcher is unable to start without it. Ensure you are connected to the internet and relaunch the application.' ,
'Close'
)
setOverlayHandler ( ( ) => {
const window = remote . getCurrentWindow ( )
window . close ( )
} )
toggleOverlay ( true )
} )
} , 750 )
}
2018-05-06 22:34:57 -07:00
2018-06-23 12:17:26 -07:00
/ * *
* Common functions to perform after refreshing the distro index .
*
* @ param { Object } data The distro index object .
* /
2018-05-08 17:10:46 -07:00
function onDistroRefresh ( data ) {
2023-02-24 18:02:18 -08:00
updateSelectedServer ( data . getServerById ( ConfigManager . getSelectedServer ( ) ) )
2018-05-08 17:10:46 -07:00
refreshServerStatus ( )
initNews ( )
2018-06-23 12:17:26 -07:00
syncModConfigurations ( data )
2022-11-27 15:13:50 -08:00
ensureJavaSettings ( data )
2018-06-23 12:17:26 -07:00
}
/ * *
* Sync the mod configurations with the distro index .
*
* @ param { Object } data The distro index object .
* /
function syncModConfigurations ( data ) {
const syncedCfgs = [ ]
2023-02-24 18:02:18 -08:00
for ( let serv of data . servers ) {
2018-06-23 12:17:26 -07:00
2023-02-24 18:02:18 -08:00
const id = serv . rawServer . id
const mdls = serv . modules
2018-07-22 08:40:15 -07:00
const cfg = ConfigManager . getModConfiguration ( id )
2018-06-23 12:17:26 -07:00
if ( cfg != null ) {
const modsOld = cfg . mods
const mods = { }
2018-07-22 08:40:15 -07:00
for ( let mdl of mdls ) {
2023-02-24 18:02:18 -08:00
const type = mdl . rawModule . type
2018-07-22 08:40:15 -07:00
2023-02-24 18:02:18 -08:00
if ( type === Type . ForgeMod || type === Type . LiteMod || type === Type . LiteLoader ) {
if ( ! mdl . getRequired ( ) . value ) {
const mdlID = mdl . getVersionlessMavenIdentifier ( )
2018-06-23 18:03:49 -07:00
if ( modsOld [ mdlID ] == null ) {
2023-02-24 18:02:18 -08:00
mods [ mdlID ] = scanOptionalSubModules ( mdl . subModules , mdl )
2018-06-23 18:03:49 -07:00
} else {
2023-02-24 18:02:18 -08:00
mods [ mdlID ] = mergeModConfiguration ( modsOld [ mdlID ] , scanOptionalSubModules ( mdl . subModules , mdl ) , false )
2018-07-29 07:32:41 -07:00
}
} else {
2023-02-24 18:02:18 -08:00
if ( mdl . subModules . length > 0 ) {
const mdlID = mdl . getVersionlessMavenIdentifier ( )
const v = scanOptionalSubModules ( mdl . subModules , mdl )
2018-07-29 07:32:41 -07:00
if ( typeof v === 'object' ) {
if ( modsOld [ mdlID ] == null ) {
mods [ mdlID ] = v
} else {
mods [ mdlID ] = mergeModConfiguration ( modsOld [ mdlID ] , v , true )
}
}
2018-06-23 12:17:26 -07:00
}
}
}
}
syncedCfgs . push ( {
id ,
mods
} )
} else {
const mods = { }
2018-07-22 08:40:15 -07:00
for ( let mdl of mdls ) {
2023-02-24 18:02:18 -08:00
const type = mdl . rawModule . type
if ( type === Type . ForgeMod || type === Type . LiteMod || type === Type . LiteLoader ) {
if ( ! mdl . getRequired ( ) . value ) {
mods [ mdl . getVersionlessMavenIdentifier ( ) ] = scanOptionalSubModules ( mdl . subModules , mdl )
2018-07-29 07:32:41 -07:00
} else {
2023-02-24 18:02:18 -08:00
if ( mdl . subModules . length > 0 ) {
const v = scanOptionalSubModules ( mdl . subModules , mdl )
2018-07-29 07:32:41 -07:00
if ( typeof v === 'object' ) {
2023-02-24 18:02:18 -08:00
mods [ mdl . getVersionlessMavenIdentifier ( ) ] = v
2018-07-29 07:32:41 -07:00
}
}
2018-06-23 12:17:26 -07:00
}
}
}
syncedCfgs . push ( {
id ,
mods
} )
}
}
ConfigManager . setModConfigurations ( syncedCfgs )
ConfigManager . save ( )
2018-05-08 17:10:46 -07:00
}
2022-11-27 15:13:50 -08:00
/ * *
* Ensure java configurations are present for the available servers .
*
* @ param { Object } data The distro index object .
* /
function ensureJavaSettings ( data ) {
// Nothing too fancy for now.
2023-02-24 18:02:18 -08:00
for ( const serv of data . servers ) {
ConfigManager . ensureJavaConfig ( serv . rawServer . id , serv . rawServer . minecraftVersion )
2022-11-27 15:13:50 -08:00
}
ConfigManager . save ( )
}
2018-06-23 18:03:49 -07:00
/ * *
* Recursively scan for optional sub modules . If none are found ,
* this function returns a boolean . If optional sub modules do exist ,
* a recursive configuration object is returned .
*
* @ returns { boolean | Object } The resolved mod configuration .
* /
function scanOptionalSubModules ( mdls , origin ) {
if ( mdls != null ) {
const mods = { }
2018-07-22 08:40:15 -07:00
for ( let mdl of mdls ) {
2023-02-24 18:02:18 -08:00
const type = mdl . rawModule . type
2018-06-23 18:03:49 -07:00
// Optional types.
2023-02-24 18:02:18 -08:00
if ( type === Type . ForgeMod || type === Type . LiteMod || type === Type . LiteLoader ) {
2018-06-23 18:03:49 -07:00
// It is optional.
2023-02-24 18:02:18 -08:00
if ( ! mdl . getRequired ( ) . value ) {
mods [ mdl . getVersionlessMavenIdentifier ( ) ] = scanOptionalSubModules ( mdl . subModules , mdl )
2018-07-29 07:32:41 -07:00
} else {
if ( mdl . hasSubModules ( ) ) {
2023-02-24 18:02:18 -08:00
const v = scanOptionalSubModules ( mdl . subModules , mdl )
2018-07-29 07:32:41 -07:00
if ( typeof v === 'object' ) {
2023-02-24 18:02:18 -08:00
mods [ mdl . getVersionlessMavenIdentifier ( ) ] = v
2018-07-29 07:32:41 -07:00
}
}
2018-06-23 18:03:49 -07:00
}
}
}
if ( Object . keys ( mods ) . length > 0 ) {
2018-07-29 07:32:41 -07:00
const ret = {
2018-06-23 18:03:49 -07:00
mods
}
2023-02-24 18:02:18 -08:00
if ( ! origin . getRequired ( ) . value ) {
ret . value = origin . getRequired ( ) . def
2018-07-29 07:32:41 -07:00
}
return ret
2018-06-23 18:03:49 -07:00
}
}
2023-02-24 18:02:18 -08:00
return origin . getRequired ( ) . def
2018-06-23 18:03:49 -07:00
}
/ * *
* Recursively merge an old configuration into a new configuration .
*
* @ param { boolean | Object } o The old configuration value .
* @ param { boolean | Object } n The new configuration value .
2018-07-29 07:32:41 -07:00
* @ param { boolean } nReq If the new value is a required mod .
2018-06-23 18:03:49 -07:00
*
* @ returns { boolean | Object } The merged configuration .
* /
2018-07-29 07:32:41 -07:00
function mergeModConfiguration ( o , n , nReq = false ) {
2018-06-23 18:03:49 -07:00
if ( typeof o === 'boolean' ) {
if ( typeof n === 'boolean' ) return o
else if ( typeof n === 'object' ) {
2018-07-29 07:32:41 -07:00
if ( ! nReq ) {
n . value = o
}
2018-06-23 18:03:49 -07:00
return n
}
} else if ( typeof o === 'object' ) {
2018-07-29 07:32:41 -07:00
if ( typeof n === 'boolean' ) return typeof o . value !== 'undefined' ? o . value : true
2018-06-23 18:03:49 -07:00
else if ( typeof n === 'object' ) {
2018-07-29 07:32:41 -07:00
if ( ! nReq ) {
n . value = typeof o . value !== 'undefined' ? o . value : true
}
2018-06-23 18:03:49 -07:00
const newMods = Object . keys ( n . mods )
for ( let i = 0 ; i < newMods . length ; i ++ ) {
const mod = newMods [ i ]
if ( o . mods [ mod ] != null ) {
n . mods [ mod ] = mergeModConfiguration ( o . mods [ mod ] , n . mods [ mod ] )
}
}
return n
}
}
// If for some reason we haven't been able to merge,
// wipe the old value and use the new one. Just to be safe
return n
}
2018-05-09 19:23:37 -07:00
async function validateSelectedAccount ( ) {
const selectedAcc = ConfigManager . getSelectedAccount ( )
if ( selectedAcc != null ) {
const val = await AuthManager . validateSelected ( )
if ( ! val ) {
2018-05-29 18:47:55 -07:00
ConfigManager . removeAuthAccount ( selectedAcc . uuid )
ConfigManager . save ( )
2018-05-09 19:23:37 -07:00
const accLen = Object . keys ( ConfigManager . getAuthAccounts ( ) ) . length
setOverlayContent (
'Failed to Refresh Login' ,
2018-05-29 18:47:55 -07:00
` We were unable to refresh the login for <strong> ${ selectedAcc . displayName } </strong>. Please ${ accLen > 0 ? 'select another account or ' : '' } login again. ` ,
2018-05-09 19:23:37 -07:00
'Login' ,
'Select Another Account'
)
setOverlayHandler ( ( ) => {
2022-02-11 16:51:28 -08:00
const isMicrosoft = selectedAcc . type === 'microsoft'
if ( isMicrosoft ) {
// Empty for now
} else {
// Mojang
// For convenience, pre-populate the username of the account.
document . getElementById ( 'loginUsername' ) . value = selectedAcc . username
validateEmail ( selectedAcc . username )
}
loginOptionsViewOnLoginSuccess = getCurrentView ( )
loginOptionsViewOnLoginCancel = VIEWS . loginOptions
if ( accLen > 0 ) {
loginOptionsViewOnCancel = getCurrentView ( )
loginOptionsViewCancelHandler = ( ) => {
if ( isMicrosoft ) {
ConfigManager . addMicrosoftAuthAccount (
selectedAcc . uuid ,
selectedAcc . accessToken ,
selectedAcc . username ,
selectedAcc . expiresAt ,
selectedAcc . microsoft . access _token ,
selectedAcc . microsoft . refresh _token ,
selectedAcc . microsoft . expires _at
)
} else {
ConfigManager . addMojangAuthAccount ( selectedAcc . uuid , selectedAcc . accessToken , selectedAcc . username , selectedAcc . displayName )
}
2018-08-22 07:54:09 -07:00
ConfigManager . save ( )
validateSelectedAccount ( )
}
2022-02-11 16:51:28 -08:00
loginOptionsCancelEnabled ( true )
} else {
loginOptionsCancelEnabled ( false )
2018-08-22 07:54:09 -07:00
}
2018-05-09 19:23:37 -07:00
toggleOverlay ( false )
2022-02-11 16:51:28 -08:00
switchView ( getCurrentView ( ) , VIEWS . loginOptions )
2018-05-09 19:23:37 -07:00
} )
setDismissHandler ( ( ) => {
2018-05-29 18:47:55 -07:00
if ( accLen > 1 ) {
2018-05-09 19:23:37 -07:00
prepareAccountSelectionList ( )
$ ( '#overlayContent' ) . fadeOut ( 250 , ( ) => {
2018-08-22 07:54:09 -07:00
bindOverlayKeys ( true , 'accountSelectContent' , true )
2018-05-09 19:23:37 -07:00
$ ( '#accountSelectContent' ) . fadeIn ( 250 )
} )
} else {
const accountsObj = ConfigManager . getAuthAccounts ( )
2018-07-22 10:31:15 -07:00
const accounts = Array . from ( Object . keys ( accountsObj ) , v => accountsObj [ v ] )
2018-05-29 18:47:55 -07:00
// This function validates the account switch.
setSelectedAccount ( accounts [ 0 ] . uuid )
toggleOverlay ( false )
2018-05-09 19:23:37 -07:00
}
} )
2018-05-29 18:47:55 -07:00
toggleOverlay ( true , accLen > 0 )
2018-05-09 19:23:37 -07:00
} else {
return true
}
} else {
return true
}
}
2018-05-09 21:01:38 -07:00
/ * *
* Temporary function to update the selected account along
* with the relevent UI elements .
*
* @ param { string } uuid The UUID of the account .
* /
2018-05-09 19:23:37 -07:00
function setSelectedAccount ( uuid ) {
const authAcc = ConfigManager . setSelectedAccount ( uuid )
ConfigManager . save ( )
updateSelectedAccount ( authAcc )
2018-05-29 18:47:55 -07:00
validateSelectedAccount ( )
2018-05-09 19:23:37 -07:00
}
2018-04-25 17:11:10 -07:00
// Synchronous Listener
2023-02-24 18:02:18 -08:00
document . addEventListener ( 'readystatechange' , async ( ) => {
2018-04-25 17:11:10 -07:00
2018-07-29 05:42:11 -07:00
if ( document . readyState === 'interactive' || document . readyState === 'complete' ) {
2018-05-06 22:34:57 -07:00
if ( rscShouldLoad ) {
2018-07-29 07:32:41 -07:00
rscShouldLoad = false
2018-05-08 03:34:16 -07:00
if ( ! fatalStartupError ) {
2023-02-24 18:02:18 -08:00
const data = await DistroAPI . getDistribution ( )
await showMainUI ( data )
2018-05-06 22:34:57 -07:00
} else {
2018-05-08 03:34:16 -07:00
showFatalStartupError ( )
2018-05-06 22:34:57 -07:00
}
2018-05-08 03:34:16 -07:00
}
2018-07-29 07:32:41 -07:00
}
2018-04-25 17:11:10 -07:00
} , false )
2018-05-06 22:34:57 -07:00
// Actions that must be performed after the distribution index is downloaded.
2023-02-24 18:02:18 -08:00
ipcRenderer . on ( 'distributionIndexDone' , async ( event , res ) => {
2018-07-22 08:40:15 -07:00
if ( res ) {
2023-02-24 18:02:18 -08:00
const data = await DistroAPI . getDistribution ( )
2018-06-23 12:17:26 -07:00
syncModConfigurations ( data )
2022-11-27 15:13:50 -08:00
ensureJavaSettings ( data )
2018-07-29 05:42:11 -07:00
if ( document . readyState === 'interactive' || document . readyState === 'complete' ) {
2023-02-24 18:02:18 -08:00
await showMainUI ( data )
2018-05-06 22:34:57 -07:00
} else {
2018-05-08 03:34:16 -07:00
rscShouldLoad = true
2018-05-06 22:34:57 -07:00
}
} else {
2018-05-08 03:34:16 -07:00
fatalStartupError = true
2018-07-29 05:42:11 -07:00
if ( document . readyState === 'interactive' || document . readyState === 'complete' ) {
2018-07-22 10:31:15 -07:00
showFatalStartupError ( )
2018-05-08 03:34:16 -07:00
} else {
rscShouldLoad = true
}
2018-05-06 22:34:57 -07:00
}
} )
2022-11-27 15:13:50 -08:00
// Util for development
2023-02-24 18:02:18 -08:00
async function devModeToggle ( ) {
DistroAPI . toggleDevMode ( true )
const data = await DistroAPI . getDistributionLocalLoadOnly ( )
ensureJavaSettings ( data )
updateSelectedServer ( data . getServers ( ) [ 0 ] )
syncModConfigurations ( data )
2022-11-27 15:13:50 -08:00
}