Merge branch 'pr/5' into MLS-v2-#2

This commit is contained in:
Peter 2021-07-10 00:25:49 +01:00
commit f14d065c74
7 changed files with 556 additions and 66 deletions

View File

@ -613,8 +613,7 @@ body, button {
font-weight: bold;
letter-spacing: 2px;
border: none;
padding: 15px 5px;
margin: 10px 0px;
margin: 15px 0px;
cursor: pointer;
position: relative;
right: -20px;
@ -689,6 +688,21 @@ body, button {
width: 16px;
height: 16px;
}
.circle-loader-big {
margin:auto;
border: 5px solid rgba(255, 255, 255, 0.5);
border-left-color: #ffffff;
animation-name: loader-spin;
animation-duration: 1s;
animation-iteration-count: infinite;
animation-timing-function: linear;
position: relative;
display: block;
align-self: center;
border-radius: 50%;
width: 60px;
height: 60px;
}
.load-complete {
animation: none;
border-color: #ffffff;
@ -859,6 +873,29 @@ body, button {
transform: rotate(45deg);
}
#loginMSButton {
background: none;
border: none;
margin-top: 5px;
margin-bottom: 10px;
font-size: 20px;
}
#loginMSButton:disabled {
color: rgba(255, 255, 255, 0.75);
pointer-events: none;
}
#loginMSButton:hover,
#loginMSButton:focus {
text-shadow: 0px 0px 20px #fff;
outline: none;
}
#loginMSButton:active {
color: #c7c7c7;
text-shadow: 0px 0px 20px #c7c7c7;
}
/*
#login_filter {
height: calc(100% - 22px);

View File

@ -12,9 +12,66 @@
const ConfigManager = require('./configmanager')
const LoggerUtil = require('./loggerutil')
const Mojang = require('./mojang')
const Microsoft = require('./microsoft')
const logger = LoggerUtil('%c[AuthManager]', 'color: #a02d2a; font-weight: bold')
const loggerSuccess = LoggerUtil('%c[AuthManager]', 'color: #209b07; font-weight: bold')
async function validateSelectedMojang() {
const current = ConfigManager.getSelectedAccount()
const isValid = await Mojang.validate(current.accessToken, ConfigManager.getClientToken())
if(!isValid){
try {
const session = await Mojang.refresh(current.accessToken, ConfigManager.getClientToken())
ConfigManager.updateAuthAccount(current.uuid, session.accessToken)
ConfigManager.save()
} catch(err) {
logger.debug('Error while validating selected profile:', err)
if(err && err.error === 'ForbiddenOperationException'){
// What do we do?
}
logger.log('Account access token is invalid.')
return false
}
loggerSuccess.log('Account access token validated.')
return true
} else {
loggerSuccess.log('Account access token validated.')
return true
}
}
async function validateSelectedMicrosoft() {
try {
const current = ConfigManager.getSelectedAccount()
const now = new Date().getTime()
const MCExpiresAt = Date.parse(current.expiresAt)
const MCExpired = now > MCExpiresAt
if(MCExpired) {
const MSExpiresAt = Date.parse(current.microsoft.expires_at)
const MSExpired = now > MSExpiresAt
if (MSExpired) {
const newAccessToken = await Microsoft.refreshAccessToken(current.microsoft.refresh_token)
const newMCAccessToken = await Microsoft.authMinecraft(newAccessToken.access_token)
ConfigManager.updateAuthAccount(current.uuid, newMCAccessToken.access_token, newAccessToken.expires_at)
ConfigManager.save()
return true
}
const newMCAccessToken = await Microsoft.authMinecraft(current.microsoft.access_token)
ConfigManager.updateAuthAccount(current.uuid, newMCAccessToken.access_token, current.microsoft.access_token, current.microsoft.expires_at, newMCAccessToken.expires_at)
ConfigManager.save()
return true
} else {
return true
}
} catch (error) {
return Promise.reject(error)
}
}
// Exports
// Functions
/**
@ -55,6 +112,11 @@ exports.addAccount = async function(username, password){
exports.removeAccount = async function(uuid){
try {
const authAcc = ConfigManager.getAuthAccount(uuid)
if(authAcc.type === 'microsoft'){
ConfigManager.removeAuthAccount(uuid)
ConfigManager.save()
return Promise.resolve()
}
await Mojang.invalidate(authAcc.accessToken, ConfigManager.getClientToken())
ConfigManager.removeAuthAccount(uuid)
ConfigManager.save()
@ -78,22 +140,35 @@ exports.validateSelected = async function(){
const current = ConfigManager.getSelectedAccount()
const isValid = await Mojang.validate(current.accessToken, ConfigManager.getClientToken())
if(!isValid){
try {
const session = await Mojang.refresh(current.accessToken, ConfigManager.getClientToken())
ConfigManager.updateAuthAccount(current.uuid, session.accessToken)
ConfigManager.save()
} catch(err) {
logger.debug('Error while validating selected profile:', err)
if(err && err.error === 'ForbiddenOperationException'){
// What do we do?
try{
if (ConfigManager.getSelectedAccount() === 'microsoft') {
const validate = await validateSelectedMicrosoft()
return validate
} else {
const validate = await validateSelectedMojang()
return validate
}
logger.log('Account access token is invalid.')
return false
} catch (error) {
return Promise.reject(error)
}
loggerSuccess.log('Account access token validated.')
return true
} else {
loggerSuccess.log('Account access token validated.')
return true
} else return true
}
exports.addMSAccount = async authCode => {
try {
const accessToken = await Microsoft.getAccessToken(authCode)
const MCAccessToken = await Microsoft.authMinecraft(accessToken.access_token)
const minecraftBuyed = await Microsoft.checkMCStore(MCAccessToken.access_token)
if(!minecraftBuyed)
return Promise.reject({
message: 'You didn\'t buy Minecraft! Please use another Microsoft account or buy Minecraft.'
})
const MCProfile = await Microsoft.getMCProfile(MCAccessToken.access_token)
const ret = ConfigManager.addMsAuthAccount(MCProfile.id, MCAccessToken.access_token, MCProfile.name, MCAccessToken.expires_at, accessToken.access_token, accessToken.refresh_token)
ConfigManager.save()
return ret
} catch(error) {
return Promise.reject(error)
}
}

View File

@ -103,7 +103,8 @@ const DEFAULT_CONFIG = {
selectedServer: null, // Resolved
selectedAccount: null,
authenticationDatabase: {},
modConfigurations: []
modConfigurations: [],
microsoftAuth: {}
}
let config = null
@ -331,7 +332,28 @@ exports.updateAuthAccount = function(uuid, accessToken){
}
/**
* Adds an authenticated account to the database to be stored.
* Update the tokens of an authenticated microsoft account.
*
* @param {string} uuid The uuid of the authenticated account.
* @param {string} accessToken The new Access Token.
* @param {string} msAccessToken The new Microsoft Access Token
* @param {string} msRefreshToken The new Microsoft Refresh Token
* @param {date} msExpires The date when the microsoft access token expires
* @param {date} mcExpires The date when the mojang access token expires
*
* @returns {Object} The authenticated account object created by this action.
*/
exports.updateAuthAccount = function(uuid, accessToken, msAccessToken, msRefreshToken, msExpires, mcExpires){
config.authenticationDatabase[uuid].accessToken = accessToken
config.authenticationDatabase[uuid].expiresAt = mcExpires
config.authenticationDatabase[uuid].microsoft.access_token = msAccessToken
config.authenticationDatabase[uuid].microsoft.refresh_token = msRefreshToken
config.authenticationDatabase[uuid].microsoft.expires_at = msRefreshToken
return config.authenticationDatabase[uuid]
}
/**
* Adds an authenticated mojang account to the database to be stored.
*
* @param {string} uuid The uuid of the authenticated account.
* @param {string} accessToken The accessToken of the authenticated account.
@ -346,7 +368,39 @@ exports.addAuthAccount = function(uuid, accessToken, username, displayName){
accessToken,
username: username.trim(),
uuid: uuid.trim(),
displayName: displayName.trim()
displayName: displayName.trim(),
type: 'mojang'
}
return config.authenticationDatabase[uuid]
}
/**
* Adds an authenticated microsoft account to the database to be stored.
*
* @param {string} uuid The uuid of the authenticated account.
* @param {string} accessToken The accessToken of the authenticated account.
* @param {string} name The in game name of the authenticated account.
* @param {date} mcExpires The date when the mojang access token expires
* @param {string} msAccessToken The microsoft access token
* @param {string} msRefreshToken The microsoft refresh token
* @param {date} msExpires The date when the microsoft access token expires
*
* @returns {Object} The authenticated account object created by this action.
*/
exports.addMsAuthAccount = function(uuid, accessToken, name, mcExpires, msAccessToken, msRefreshToken, msExpires){
config.selectedAccount = uuid
config.authenticationDatabase[uuid] = {
accessToken,
username: name.trim(),
uuid: uuid.trim(),
displayName: name.trim(),
expiresAt: mcExpires,
type: 'microsoft',
microsoft: {
access_token: msAccessToken,
refresh_token: msRefreshToken,
expires_at: msExpires
}
}
return config.authenticationDatabase[uuid]
}

225
app/assets/js/microsoft.js Normal file
View File

@ -0,0 +1,225 @@
// Requirements
const request = require('request')
// Constants
const clientId = 'client id here'
const tokenUri = 'https://login.microsoftonline.com/consumers/oauth2/v2.0/token'
const authXBLUri = 'https://user.auth.xboxlive.com/user/authenticate'
const authXSTSUri = 'https://xsts.auth.xboxlive.com/xsts/authorize'
const authMCUri = 'https://api.minecraftservices.com/authentication/login_with_xbox'
const profileURI = 'https://api.minecraftservices.com/minecraft/profile'
// Functions
function requestPromise(uri, options) {
return new Promise((resolve, reject) => {
request(uri, options, (error, response, body) => {
if (error) {
reject(error)
} else if (response.statusCode !== 200) {
reject([response.statusCode, response.statusMessage, response])
} else {
resolve(response)
}
})
})
}
function getXBLToken(accessToken) {
return new Promise((resolve, reject) => {
const data = new Object()
const options = {
method: 'post',
json: {
Properties: {
AuthMethod: 'RPS',
SiteName: 'user.auth.xboxlive.com',
RpsTicket: `d=${accessToken}`
},
RelyingParty: 'http://auth.xboxlive.com',
TokenType: 'JWT'
}
}
requestPromise(authXBLUri, options).then(response => {
const body = response.body
data.token = body.Token
data.uhs = body.DisplayClaims.xui[0].uhs
resolve(data)
}).catch(error => {
reject(error)
})
})
}
function getXSTSToken(XBLToken) {
return new Promise((resolve, reject) => {
const options = {
method: 'post',
json: {
Properties: {
SandboxId: 'RETAIL',
UserTokens: [XBLToken]
},
RelyingParty: 'rp://api.minecraftservices.com/',
TokenType: 'JWT'
}
}
requestPromise(authXSTSUri, options).then(response => {
if (response.body.XErr) {
switch (response.body.XErr) {
case 2148916233:
reject({
message: 'Your Microsoft account is not connected to an Xbox account. Please create one.<br>'
})
return
case 2148916238:
reject({
message: 'Since you are not yet 18 years old, an adult must add you to a family in order for you to use Helios Launcher!'
})
return
}
reject(response.body)
}
resolve(response.body.Token)
}).catch(error => {
reject(error)
})
})
}
function getMCAccessToken(UHS, XSTSToken) {
return new Promise((resolve, reject) => {
const data = new Object()
const expiresAt = new Date()
const options = {
method: 'post',
json: {
identityToken: `XBL3.0 x=${UHS};${XSTSToken}`
}
}
requestPromise(authMCUri, options).then(response => {
const body = response.body
expiresAt.setSeconds(expiresAt.getSeconds() + body.expires_in)
data.access_token = body.access_token
data.expires_at = expiresAt
resolve(data)
}).catch(error => {
reject(error)
})
})
}
// Exports
exports.getAccessToken = authCode => {
return new Promise((resolve, reject) => {
const expiresAt = new Date()
const data = new Object()
const options = {
method: 'post',
formData: {
client_id: clientId,
code: authCode,
scope: 'XboxLive.signin',
redirect_uri: 'https://login.microsoftonline.com/common/oauth2/nativeclient',
grant_type: 'authorization_code'
}
}
requestPromise(tokenUri, options).then(response => {
const body = JSON.parse(response.body)
expiresAt.setSeconds(expiresAt.getSeconds() + body.expires_in)
data.expires_at = expiresAt
data.access_token = body.access_token
data.refresh_token = body.refresh_token
resolve(data)
}).catch(error => {
reject(error)
})
})
}
exports.refreshAccessToken = refreshToken => {
return new Promise((resolve, reject) => {
const expiresAt = new Date()
const data = new Object()
const options = {
method: 'post',
formData: {
client_id: clientId,
refresh_token: refreshToken,
scope: 'XboxLive.signin',
redirect_uri: 'https://login.microsoftonline.com/common/oauth2/nativeclient',
grant_type: 'refresh_token'
}
}
requestPromise(tokenUri, options).then(response => {
const body = JSON.parse(response.body)
expiresAt.setSeconds(expiresAt.getSeconds() + body.expires_in)
data.expires_at = expiresAt
data.access_token = body.access_token
resolve(data)
}).catch(error => {
reject(error)
})
})
}
exports.authMinecraft = async accessToken => {
try {
const XBLToken = await getXBLToken(accessToken)
const XSTSToken = await getXSTSToken(XBLToken.token)
const MCToken = await getMCAccessToken(XBLToken.uhs, XSTSToken)
return MCToken
} catch (error) {
Promise.reject(error)
}
}
exports.checkMCStore = async function(access_token){
return new Promise((resolve, reject) => {
request.get({
url: 'https://api.minecraftservices.com/entitlements/mcstore',
json: true,
headers: {
Authorization: 'Bearer ' + access_token
}
}, (err, res, body) => {
if (err) {
resolve(false)
return
}
if(body.items && body.items.length > 0) resolve(true)
else resolve(false)
})
})
}
exports.getMCProfile = MCAccessToken => {
return new Promise((resolve, reject) => {
const options = {
method: 'get',
headers: {
Authorization: `Bearer ${MCAccessToken}`
}
}
requestPromise(profileURI, options).then(response => {
const body = JSON.parse(response.body)
resolve(body)
}).catch(error => {
reject(error)
})
})
}

View File

@ -17,6 +17,7 @@ const checkmarkContainer = document.getElementById('checkmarkContainer')
const loginRememberOption = document.getElementById('loginRememberOption')
const loginButton = document.getElementById('loginButton')
const loginForm = document.getElementById('loginForm')
const loginMSButton = document.getElementById('loginMSButton')
// Control variables.
let lu = false, lp = false
@ -297,4 +298,97 @@ loginButton.addEventListener('click', () => {
loggerLogin.log('Error while logging in.', err)
})
})
})
loginMSButton.addEventListener('click', (event) => {
// Show loading stuff.
toggleOverlay(true, false, 'msOverlay')
loginMSButton.disabled = true
ipcRenderer.send('openMSALoginWindow', 'open')
})
ipcRenderer.on('MSALoginWindowReply', (event, ...args) => {
if (args[0] === 'error') {
loginMSButton.disabled = false
loginLoading(false)
switch (args[1]){
case 'AlreadyOpenException': {
setOverlayContent('ERROR', 'There is already a login window open!', 'OK')
setOverlayHandler(() => {
toggleOverlay(false)
toggleOverlay(false, false, 'msOverlay')
})
toggleOverlay(true)
return
}
case 'AuthNotFinished': {
setOverlayContent('ERROR', 'You have to finish the login process to use Helios Launcher. The window will close by itself when you have successfully logged in.', 'OK')
setOverlayHandler(() => {
toggleOverlay(false)
toggleOverlay(false, false, 'msOverlay')
})
toggleOverlay(true)
return
}
}
}
toggleOverlay(false, false, 'msOverlay')
const queryMap = args[0]
if (queryMap.has('error')) {
let error = queryMap.get('error')
let errorDesc = queryMap.get('error_description')
if(error === 'access_denied'){
error = 'ERRPR'
errorDesc = 'To use the Helios Launcher, you must agree to the required permissions! Otherwise you can\'t use this launcher with Microsoft accounts.<br><br>Despite agreeing to the permissions you don\'t give us the possibility to do anything with your account, because all data will always be sent back to you (the launcher) IMMEDIATELY and WITHOUT WAY.'
}
setOverlayContent(error, errorDesc, 'OK')
setOverlayHandler(() => {
loginMSButton.disabled = false
toggleOverlay(false)
})
toggleOverlay(true)
return
}
// Disable form.
formDisabled(true)
const authCode = queryMap.get('code')
AuthManager.addMSAccount(authCode).then(account => {
updateSelectedAccount(account)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.success'))
$('.circle-loader').toggleClass('load-complete')
$('.checkmark').toggle()
setTimeout(() => {
switchView(VIEWS.login, loginViewOnSuccess, 500, 500, () => {
// Temporary workaround
if (loginViewOnSuccess === VIEWS.settings) {
prepareSettings()
}
loginViewOnSuccess = VIEWS.landing // Reset this for good measure.
loginCancelEnabled(false) // Reset this for good measure.
loginViewCancelHandler = null // Reset this for good measure.
loginUsername.value = ''
loginPassword.value = ''
$('.circle-loader').toggleClass('load-complete')
$('.checkmark').toggle()
loginLoading(false)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.success'), Lang.queryJS('login.login'))
formDisabled(false)
})
}, 1000)
}).catch(error => {
loginMSButton.disabled = false
loginLoading(false)
setOverlayContent('ERROR', error.message ? error.message : 'An error occurred while logging in with Microsoft! For more detailed information please check the log. You can open it with CTRL + SHIFT + I.', Lang.queryJS('login.tryAgain'))
setOverlayHandler(() => {
formDisabled(false)
toggleOverlay(false)
})
toggleOverlay(true)
loggerLogin.error(error)
})
})

View File

@ -9,7 +9,7 @@
*
* @returns {boolean} Whether or not the overlay is visible.
*/
function isOverlayVisible(){
function isOverlayVisible() {
return document.getElementById('main').hasAttribute('overlay')
}
@ -20,8 +20,8 @@ let overlayHandlerContent
*
* @param {KeyboardEvent} e The keydown event.
*/
function overlayKeyHandler (e){
if(e.key === 'Enter' || e.key === 'Escape'){
function overlayKeyHandler(e) {
if (e.key === 'Enter' || e.key === 'Escape') {
document.getElementById(overlayHandlerContent).getElementsByClassName('overlayKeybindEnter')[0].click()
}
}
@ -30,10 +30,10 @@ function overlayKeyHandler (e){
*
* @param {KeyboardEvent} e The keydown event.
*/
function overlayKeyDismissableHandler (e){
if(e.key === 'Enter'){
function overlayKeyDismissableHandler(e) {
if (e.key === 'Enter') {
document.getElementById(overlayHandlerContent).getElementsByClassName('overlayKeybindEnter')[0].click()
} else if(e.key === 'Escape'){
} else if (e.key === 'Escape') {
document.getElementById(overlayHandlerContent).getElementsByClassName('overlayKeybindEsc')[0].click()
}
}
@ -45,12 +45,12 @@ function overlayKeyDismissableHandler (e){
* @param {string} content The overlay content which will be shown.
* @param {boolean} dismissable Whether or not the overlay is dismissable
*/
function bindOverlayKeys(state, content, dismissable){
function bindOverlayKeys(state, content, dismissable) {
overlayHandlerContent = content
document.removeEventListener('keydown', overlayKeyHandler)
document.removeEventListener('keydown', overlayKeyDismissableHandler)
if(state){
if(dismissable){
if (state) {
if (dismissable) {
document.addEventListener('keydown', overlayKeyDismissableHandler)
} else {
document.addEventListener('keydown', overlayKeyHandler)
@ -65,22 +65,22 @@ function bindOverlayKeys(state, content, dismissable){
* @param {boolean} dismissable Optional. True to show the dismiss option, otherwise false.
* @param {string} content Optional. The content div to be shown.
*/
function toggleOverlay(toggleState, dismissable = false, content = 'overlayContent'){
if(toggleState == null){
function toggleOverlay(toggleState, dismissable = false, content = 'overlayContent') {
if (toggleState == null) {
toggleState = !document.getElementById('main').hasAttribute('overlay')
}
if(typeof dismissable === 'string'){
if (typeof dismissable === 'string') {
content = dismissable
dismissable = false
}
bindOverlayKeys(toggleState, content, dismissable)
if(toggleState){
if (toggleState) {
document.getElementById('main').setAttribute('overlay', true)
// Make things untabbable.
$('#main *').attr('tabindex', '-1')
$('#' + content).parent().children().hide()
$('#' + content).show()
if(dismissable){
if (dismissable) {
$('#overlayDismiss').show()
} else {
$('#overlayDismiss').hide()
@ -88,7 +88,7 @@ function toggleOverlay(toggleState, dismissable = false, content = 'overlayConte
$('#overlayContainer').fadeIn({
duration: 250,
start: () => {
if(getCurrentView() === VIEWS.settings){
if (getCurrentView() === VIEWS.settings) {
document.getElementById('settingsContainer').style.backgroundColor = 'transparent'
}
}
@ -100,14 +100,14 @@ function toggleOverlay(toggleState, dismissable = false, content = 'overlayConte
$('#overlayContainer').fadeOut({
duration: 250,
start: () => {
if(getCurrentView() === VIEWS.settings){
if (getCurrentView() === VIEWS.settings) {
document.getElementById('settingsContainer').style.backgroundColor = 'rgba(0, 0, 0, 0.50)'
}
},
complete: () => {
$('#' + content).parent().children().hide()
$('#' + content).show()
if(dismissable){
if (dismissable) {
$('#overlayDismiss').show()
} else {
$('#overlayDismiss').hide()
@ -117,7 +117,7 @@ function toggleOverlay(toggleState, dismissable = false, content = 'overlayConte
}
}
function toggleServerSelection(toggleState){
function toggleServerSelection(toggleState) {
prepareServerSelectionList()
toggleOverlay(toggleState, true, 'serverSelectContent')
}
@ -130,7 +130,7 @@ function toggleServerSelection(toggleState){
* @param {string} acknowledge Acknowledge button text.
* @param {string} dismiss Dismiss button text.
*/
function setOverlayContent(title, description, acknowledge, dismiss = 'Dismiss'){
function setOverlayContent(title, description, acknowledge, dismiss = 'Dismiss') {
document.getElementById('overlayTitle').innerHTML = title
document.getElementById('overlayDesc').innerHTML = description
document.getElementById('overlayAcknowledge').innerHTML = acknowledge
@ -143,8 +143,8 @@ function setOverlayContent(title, description, acknowledge, dismiss = 'Dismiss')
*
* @param {function} handler
*/
function setOverlayHandler(handler){
if(handler == null){
function setOverlayHandler(handler) {
if (handler == null) {
document.getElementById('overlayAcknowledge').onclick = () => {
toggleOverlay(false)
}
@ -159,8 +159,8 @@ function setOverlayHandler(handler){
*
* @param {function} handler
*/
function setDismissHandler(handler){
if(handler == null){
function setDismissHandler(handler) {
if (handler == null) {
document.getElementById('overlayDismiss').onclick = () => {
toggleOverlay(false)
}
@ -173,8 +173,8 @@ function setDismissHandler(handler){
document.getElementById('serverSelectConfirm').addEventListener('click', () => {
const listings = document.getElementsByClassName('serverListing')
for(let i=0; i<listings.length; i++){
if(listings[i].hasAttribute('selected')){
for (let i = 0; i < listings.length; i++) {
if (listings[i].hasAttribute('selected')) {
const serv = DistroManager.getDistribution().getServer(listings[i].getAttribute('servid'))
updateSelectedServer(serv)
refreshServerStatus(true)
@ -183,7 +183,7 @@ document.getElementById('serverSelectConfirm').addEventListener('click', () => {
}
}
// None are selected? Not possible right? Meh, handle it.
if(listings.length > 0){
if (listings.length > 0) {
const serv = DistroManager.getDistribution().getServer(listings[i].getAttribute('servid'))
updateSelectedServer(serv)
toggleOverlay(false)
@ -192,8 +192,8 @@ document.getElementById('serverSelectConfirm').addEventListener('click', () => {
document.getElementById('accountSelectConfirm').addEventListener('click', () => {
const listings = document.getElementsByClassName('accountListing')
for(let i=0; i<listings.length; i++){
if(listings[i].hasAttribute('selected')){
for (let i = 0; i < listings.length; i++) {
if (listings[i].hasAttribute('selected')) {
const authAcc = ConfigManager.setSelectedAccount(listings[i].getAttribute('uuid'))
ConfigManager.save()
updateSelectedAccount(authAcc)
@ -203,7 +203,7 @@ document.getElementById('accountSelectConfirm').addEventListener('click', () =>
}
}
// None are selected? Not possible right? Meh, handle it.
if(listings.length > 0){
if (listings.length > 0) {
const authAcc = ConfigManager.setSelectedAccount(listings[0].getAttribute('uuid'))
ConfigManager.save()
updateSelectedAccount(authAcc)
@ -223,16 +223,16 @@ document.getElementById('accountSelectCancel').addEventListener('click', () => {
})
})
function setServerListingHandlers(){
function setServerListingHandlers() {
const listings = Array.from(document.getElementsByClassName('serverListing'))
listings.map((val) => {
val.onclick = e => {
if(val.hasAttribute('selected')){
if (val.hasAttribute('selected')) {
return
}
const cListings = document.getElementsByClassName('serverListing')
for(let i=0; i<cListings.length; i++){
if(cListings[i].hasAttribute('selected')){
for (let i = 0; i < cListings.length; i++) {
if (cListings[i].hasAttribute('selected')) {
cListings[i].removeAttribute('selected')
}
}
@ -242,16 +242,16 @@ function setServerListingHandlers(){
})
}
function setAccountListingHandlers(){
function setAccountListingHandlers() {
const listings = Array.from(document.getElementsByClassName('accountListing'))
listings.map((val) => {
val.onclick = e => {
if(val.hasAttribute('selected')){
if (val.hasAttribute('selected')) {
return
}
const cListings = document.getElementsByClassName('accountListing')
for(let i=0; i<cListings.length; i++){
if(cListings[i].hasAttribute('selected')){
for (let i = 0; i < cListings.length; i++) {
if (cListings[i].hasAttribute('selected')) {
cListings[i].removeAttribute('selected')
}
}
@ -261,12 +261,12 @@ function setAccountListingHandlers(){
})
}
function populateServerListings(){
function populateServerListings() {
const distro = DistroManager.getDistribution()
const giaSel = ConfigManager.getSelectedServer()
const servers = distro.getServers()
let htmlString = ''
for(const serv of servers){
for (const serv of servers) {
htmlString += `<button class="serverListing" servid="${serv.getID()}" ${serv.getID() === giaSel ? 'selected' : ''}>
<img class="serverListingImg" src="${serv.getIcon()}"/>
<div class="serverListingDetails">
@ -293,12 +293,12 @@ function populateServerListings(){
}
function populateAccountListings(){
function populateAccountListings() {
const accountsObj = ConfigManager.getAuthAccounts()
const accounts = Array.from(Object.keys(accountsObj), v=>accountsObj[v])
const accounts = Array.from(Object.keys(accountsObj), v => accountsObj[v])
let htmlString = ''
for(let i=0; i<accounts.length; i++){
htmlString += `<button class="accountListing" uuid="${accounts[i].uuid}" ${i===0 ? 'selected' : ''}>
for (let i = 0; i < accounts.length; i++) {
htmlString += `<button class="accountListing" uuid="${accounts[i].uuid}" ${i === 0 ? 'selected' : ''}>
<img src="https://crafatar.com/renders/head/${accounts[i].uuid}?scale=2&default=MHF_Steve&overlay">
<div class="accountListingName">${accounts[i].displayName}</div>
</button>`
@ -307,12 +307,12 @@ function populateAccountListings(){
}
function prepareServerSelectionList(){
function prepareServerSelectionList() {
populateServerListings()
setServerListingHandlers()
}
function prepareAccountSelectionList(){
function prepareAccountSelectionList() {
populateAccountListings()
setAccountListingHandlers()
}

View File

@ -28,6 +28,7 @@
</div>
</div>
<div id="overlayContent">
<div class="circle-loader" style="display: none;"></div>
<span id="overlayTitle">Lorem Ipsum:<br>Finis Illud</span>
<span id="overlayDesc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud..</span>
<div id="overlayActionContainer">
@ -37,5 +38,9 @@
</div>
</div>
</div>
<div id="msOverlay" style="display: none;">
<span><div class="circle-loader-big"></div></span><br>
<span style="font-size: 2em;">Waiting on Microsoft...</span>
</div>
<script src="./assets/js/scripts/overlay.js"></script>
</div>