Polish launcher main and library flows
This commit is contained in:
@@ -227,10 +227,10 @@ async function renderInstallView(){
|
||||
const detailButton = document.createElement('button')
|
||||
detailButton.className = 'launcherSecondaryButton'
|
||||
detailButton.textContent = '자세히 보기'
|
||||
detailButton.addEventListener('click', (event) => {
|
||||
detailButton.addEventListener('click', async (event) => {
|
||||
event.stopPropagation()
|
||||
selectProfile(profile.id)
|
||||
renderInstallView()
|
||||
await renderInstallView()
|
||||
})
|
||||
|
||||
const installButton = document.createElement('button')
|
||||
@@ -248,6 +248,9 @@ async function renderInstallView(){
|
||||
if(typeof refreshSelectedProfileButton === 'function'){
|
||||
refreshSelectedProfileButton()
|
||||
}
|
||||
if(typeof refreshServerStatus === 'function'){
|
||||
refreshServerStatus(true)
|
||||
}
|
||||
selectProfile(profile.id)
|
||||
await renderInstallView()
|
||||
if(typeof refreshLibraryView === 'function'){
|
||||
@@ -270,9 +273,9 @@ async function renderInstallView(){
|
||||
main.appendChild(description)
|
||||
row.appendChild(main)
|
||||
row.appendChild(actions)
|
||||
row.addEventListener('click', () => {
|
||||
row.addEventListener('click', async () => {
|
||||
selectProfile(profile.id)
|
||||
renderInstallView()
|
||||
await renderInstallView()
|
||||
})
|
||||
installCatalogList.appendChild(row)
|
||||
}
|
||||
@@ -300,22 +303,10 @@ async function renderInstallView(){
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('installOpenSettingsButton').addEventListener('click', async () => {
|
||||
await prepareSettings()
|
||||
switchView(getCurrentView(), VIEWS.settings)
|
||||
})
|
||||
|
||||
document.getElementById('installBackButton').addEventListener('click', () => {
|
||||
switchView(getCurrentView(), VIEWS.landing)
|
||||
})
|
||||
|
||||
document.getElementById('installBackToLibraryButton').addEventListener('click', async () => {
|
||||
if(typeof refreshLibraryView === 'function'){
|
||||
await refreshLibraryView()
|
||||
}
|
||||
switchView(getCurrentView(), VIEWS.library)
|
||||
})
|
||||
|
||||
document.getElementById('installDetailOpenLibraryButton').addEventListener('click', async () => {
|
||||
if(typeof refreshLibraryView === 'function'){
|
||||
await refreshLibraryView()
|
||||
|
||||
@@ -187,6 +187,19 @@ function refreshSelectedProfileButton(){
|
||||
setLaunchEnabled(selectedProfile.configured !== false)
|
||||
}
|
||||
|
||||
function isSelectedMapReady(profile){
|
||||
if(profile == null || profile.kind !== 'map'){
|
||||
return false
|
||||
}
|
||||
|
||||
const assetState = ConfigManager.getLibraryProfileAssetState(profile.id)
|
||||
return profile.launchReady && (
|
||||
assetState.worldInstalledAt != null ||
|
||||
assetState.prefetchedAt != null ||
|
||||
profile.worldArchiveUrl == null
|
||||
)
|
||||
}
|
||||
|
||||
// Bind launch button
|
||||
document.getElementById('launch_button').addEventListener('click', async e => {
|
||||
loggerLanding.info('Launching game..')
|
||||
@@ -253,20 +266,6 @@ document.getElementById('settingsMediaButton').onclick = async e => {
|
||||
switchView(getCurrentView(), VIEWS.settings)
|
||||
}
|
||||
|
||||
document.getElementById('landingLibraryButton').onclick = async () => {
|
||||
if(typeof refreshLibraryView === 'function'){
|
||||
await refreshLibraryView()
|
||||
}
|
||||
switchView(getCurrentView(), VIEWS.library)
|
||||
}
|
||||
|
||||
document.getElementById('landingInstallButton').onclick = async () => {
|
||||
if(typeof refreshInstallView === 'function'){
|
||||
await refreshInstallView()
|
||||
}
|
||||
switchView(getCurrentView(), VIEWS.install)
|
||||
}
|
||||
|
||||
avatarMenuButton.addEventListener('click', (e) => {
|
||||
e.stopPropagation()
|
||||
setAccountMenuOpen(accountMenu.hasAttribute('hidden'))
|
||||
@@ -391,15 +390,56 @@ const refreshMojangStatuses = async function(){
|
||||
|
||||
const refreshServerStatus = async (fade = false) => {
|
||||
loggerLanding.info('Refreshing Server Status')
|
||||
const serv = (await DistroAPI.getDistribution()).getServerById(ConfigManager.getSelectedServer())
|
||||
const selectedProfile = CatalogManager.getSelectedProfileSync()
|
||||
|
||||
let pLabel = Lang.queryJS('landing.serverStatus.server')
|
||||
let pVal = Lang.queryJS('landing.serverStatus.offline')
|
||||
let pLabel = Lang.queryJS('landing.profileStatus.label')
|
||||
let pVal = Lang.queryJS('landing.selectedProfile.noSelection')
|
||||
|
||||
if(selectedProfile == null){
|
||||
if(fade){
|
||||
$('#server_status_wrapper').fadeOut(250, () => {
|
||||
document.getElementById('landingPlayerLabel').innerHTML = pLabel
|
||||
document.getElementById('player_count').innerHTML = pVal
|
||||
$('#server_status_wrapper').fadeIn(500)
|
||||
})
|
||||
} else {
|
||||
document.getElementById('landingPlayerLabel').innerHTML = pLabel
|
||||
document.getElementById('player_count').innerHTML = pVal
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if(selectedProfile.kind === 'map'){
|
||||
pLabel = Lang.queryJS('landing.mapStatus.label')
|
||||
pVal = isSelectedMapReady(selectedProfile)
|
||||
? Lang.queryJS('landing.mapStatus.ready')
|
||||
: Lang.queryJS('landing.mapStatus.notReady')
|
||||
|
||||
if(fade){
|
||||
$('#server_status_wrapper').fadeOut(250, () => {
|
||||
document.getElementById('landingPlayerLabel').innerHTML = pLabel
|
||||
document.getElementById('player_count').innerHTML = pVal
|
||||
$('#server_status_wrapper').fadeIn(500)
|
||||
})
|
||||
} else {
|
||||
document.getElementById('landingPlayerLabel').innerHTML = pLabel
|
||||
document.getElementById('player_count').innerHTML = pVal
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
pLabel = Lang.queryJS('landing.serverStatus.server')
|
||||
pVal = Lang.queryJS('landing.serverStatus.offline')
|
||||
|
||||
try {
|
||||
const distro = await DistroAPI.getDistribution()
|
||||
const serv = distro?.getServerById(ConfigManager.getSelectedServer())
|
||||
?? (typeof distro?.getMainServer === 'function' ? distro.getMainServer() : null)
|
||||
if(serv == null){
|
||||
throw new Error('No server available for selected profile.')
|
||||
}
|
||||
|
||||
const servStat = await getServerStatus(47, serv.hostname, serv.port)
|
||||
console.log(servStat)
|
||||
pLabel = Lang.queryJS('landing.serverStatus.players')
|
||||
pVal = servStat.players.online + '/' + servStat.players.max
|
||||
|
||||
@@ -857,24 +897,12 @@ function slide_(up){
|
||||
}
|
||||
}
|
||||
|
||||
// Bind news button.
|
||||
// Bind install button on the landing footer.
|
||||
document.getElementById('newsButton').onclick = () => {
|
||||
// Toggle tabbing.
|
||||
if(newsActive){
|
||||
$('#landingContainer *').removeAttr('tabindex')
|
||||
$('#newsContainer *').attr('tabindex', '-1')
|
||||
} else {
|
||||
$('#landingContainer *').attr('tabindex', '-1')
|
||||
$('#newsContainer, #newsContainer *, #lower, #lower #center *').removeAttr('tabindex')
|
||||
if(newsAlertShown){
|
||||
$('#newsButtonAlert').fadeOut(2000)
|
||||
newsAlertShown = false
|
||||
ConfigManager.setNewsCacheDismissed(true)
|
||||
ConfigManager.save()
|
||||
}
|
||||
if(typeof refreshInstallView === 'function'){
|
||||
refreshInstallView()
|
||||
}
|
||||
slide_(!newsActive)
|
||||
newsActive = !newsActive
|
||||
switchView(getCurrentView(), VIEWS.install)
|
||||
}
|
||||
|
||||
// Array to store article meta.
|
||||
@@ -1073,12 +1101,6 @@ document.addEventListener('keydown', (e) => {
|
||||
// if(e.key === 'ArrowDown'){
|
||||
// document.getElementById('newsButton').click()
|
||||
// }
|
||||
} else {
|
||||
if(getCurrentView() === VIEWS.landing){
|
||||
if(e.key === 'ArrowUp'){
|
||||
document.getElementById('newsButton').click()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -89,6 +89,20 @@ function describeAssetState(profile){
|
||||
return '추가 자산 없음'
|
||||
}
|
||||
|
||||
function isProfileInstalled(profile){
|
||||
const state = ConfigManager.getLibraryProfileAssetState(profile.id)
|
||||
|
||||
if(profile.kind === 'map'){
|
||||
return state.prefetchedAt != null || profile.worldArchiveUrl == null
|
||||
}
|
||||
|
||||
if(profile.kind === 'server-pack'){
|
||||
return state.serverBundleInstalledAt != null || state.prefetchedAt != null || profile.serverBundleUrl == null
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async function prepareProfileAssets(profile){
|
||||
try {
|
||||
await ProfileAssetManager.prefetchProfileAssets(profile)
|
||||
@@ -103,6 +117,27 @@ async function prepareProfileAssets(profile){
|
||||
}
|
||||
}
|
||||
|
||||
async function applyProfileSelection(profile){
|
||||
CatalogManager.selectProfile(profile.id)
|
||||
CatalogManager.applyConfiguredProfile()
|
||||
|
||||
const distro = await DistroAPI.refreshDistributionOrFallback()
|
||||
if(distro == null){
|
||||
throw new Error('Distribution refresh returned null.')
|
||||
}
|
||||
|
||||
const currentServer = distro.getServerById(ConfigManager.getSelectedServer())
|
||||
if(currentServer == null && typeof distro.getMainServer === 'function'){
|
||||
const mainServer = distro.getMainServer()
|
||||
if(mainServer != null){
|
||||
ConfigManager.setSelectedServer(mainServer.rawServer.id)
|
||||
ConfigManager.save()
|
||||
}
|
||||
}
|
||||
|
||||
onDistroRefresh(distro)
|
||||
}
|
||||
|
||||
async function activateProfile(profile, launchNow = false){
|
||||
if(!profile.configured){
|
||||
const firstIssue = profile.launchIssues?.[0] ?? '이 프로필은 아직 실행 조건이 충족되지 않았습니다.'
|
||||
@@ -264,101 +299,39 @@ async function renderLibraryView(){
|
||||
|
||||
const description = createParagraph('launcherCardDescription', profile.description || '설명이 없습니다.')
|
||||
|
||||
const infoBlock = document.createElement('div')
|
||||
infoBlock.className = 'launcherInfoBlock'
|
||||
infoBlock.appendChild(createInfoLine('자료 상태', describeAssetState(profile)))
|
||||
infoBlock.appendChild(createInfoLine('실행 준비', profile.launchReady ? '완료' : '추가 설정 필요'))
|
||||
if(profile.defaultServerAddress){
|
||||
infoBlock.appendChild(createInfoLine('기본 주소', profile.defaultServerAddress))
|
||||
}
|
||||
if(profile.kind === 'server-pack'){
|
||||
infoBlock.appendChild(createInfoLine('로컬 호스팅', profile.hostReady ? '가능' : '서버 번들 필요'))
|
||||
}
|
||||
if(hostState.running){
|
||||
infoBlock.appendChild(createInfoLine('호스트 상태', hostState.tunneling ? '터널 연결 중' : '로컬 서버 실행 중'))
|
||||
}
|
||||
if(profile.launchIssues?.length > 0){
|
||||
infoBlock.appendChild(createInfoLine('확인 필요', profile.launchIssues[0]))
|
||||
} else if(profile.hostIssues?.length > 0){
|
||||
infoBlock.appendChild(createInfoLine('호스팅 확인', profile.hostIssues[0]))
|
||||
}
|
||||
|
||||
const fieldGroup = document.createElement('div')
|
||||
fieldGroup.className = 'launcherFieldGroup'
|
||||
appendAddressOverrideField(profile, fieldGroup)
|
||||
appendPublishedAddressField(profile, hostState, fieldGroup)
|
||||
|
||||
const actions = document.createElement('div')
|
||||
actions.className = 'launcherCardActions'
|
||||
|
||||
const prepareButton = document.createElement('button')
|
||||
prepareButton.className = 'launcherSecondaryButton'
|
||||
prepareButton.textContent = '자료 준비'
|
||||
prepareButton.addEventListener('click', async () => {
|
||||
const installButton = document.createElement('button')
|
||||
installButton.className = 'launcherSecondaryButton'
|
||||
installButton.textContent = isProfileInstalled(profile) ? '설치됨' : '설치'
|
||||
installButton.disabled = isProfileInstalled(profile) || !profile.configured
|
||||
installButton.addEventListener('click', async () => {
|
||||
await prepareProfileAssets(profile)
|
||||
})
|
||||
|
||||
const selectButton = document.createElement('button')
|
||||
selectButton.className = 'launcherSecondaryButton'
|
||||
selectButton.textContent = '프로필 선택'
|
||||
selectButton.disabled = !profile.configured
|
||||
selectButton.textContent = profile.id === selectedProfileId ? '선택됨' : '선택'
|
||||
selectButton.disabled = profile.id === selectedProfileId
|
||||
selectButton.addEventListener('click', async () => {
|
||||
CatalogManager.selectProfile(profile.id)
|
||||
CatalogManager.applyConfiguredProfile()
|
||||
if(typeof refreshSelectedProfileButton === 'function'){
|
||||
refreshSelectedProfileButton()
|
||||
}
|
||||
await renderLibraryView()
|
||||
})
|
||||
|
||||
const openButton = document.createElement('button')
|
||||
openButton.className = 'launcherSecondaryButton'
|
||||
openButton.textContent = '실행 화면'
|
||||
openButton.disabled = !profile.configured
|
||||
openButton.addEventListener('click', async () => {
|
||||
await activateProfile(profile, false)
|
||||
})
|
||||
|
||||
const launchButton = document.createElement('button')
|
||||
launchButton.className = 'launcherPrimaryButton'
|
||||
launchButton.textContent = profile.kind === 'map' ? '맵 실행' : '바로 실행'
|
||||
launchButton.disabled = !profile.configured
|
||||
launchButton.addEventListener('click', async () => {
|
||||
await activateProfile(profile, true)
|
||||
})
|
||||
|
||||
actions.appendChild(prepareButton)
|
||||
actions.appendChild(selectButton)
|
||||
actions.appendChild(openButton)
|
||||
actions.appendChild(launchButton)
|
||||
|
||||
if(profile.kind === 'server-pack'){
|
||||
const startHostButton = document.createElement('button')
|
||||
startHostButton.className = 'launcherSecondaryButton'
|
||||
startHostButton.textContent = '서버 실행'
|
||||
startHostButton.disabled = hostState.running || !profile.hostReady
|
||||
startHostButton.addEventListener('click', async () => {
|
||||
try {
|
||||
await ServerRuntime.startHostedProfile(profile)
|
||||
await renderLibraryView()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
showLibraryMessage('서버 실행 실패', '서버 번들이 준비되지 않았거나 시작 명령을 찾지 못했습니다.')
|
||||
try {
|
||||
await applyProfileSelection(profile)
|
||||
if(typeof refreshSelectedProfileButton === 'function'){
|
||||
refreshSelectedProfileButton()
|
||||
}
|
||||
if(typeof refreshServerStatus === 'function'){
|
||||
refreshServerStatus(true)
|
||||
}
|
||||
})
|
||||
|
||||
const stopHostButton = document.createElement('button')
|
||||
stopHostButton.className = 'launcherGhostButton'
|
||||
stopHostButton.textContent = '서버 중지'
|
||||
stopHostButton.disabled = !hostState.running
|
||||
stopHostButton.addEventListener('click', async () => {
|
||||
ServerRuntime.stopHostedProfile(profile.id)
|
||||
await renderLibraryView()
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
showLibraryMessage('프로필 선택 실패', '선택한 프로필의 배포 정보 또는 서버 정보를 불러오지 못했습니다.')
|
||||
}
|
||||
})
|
||||
|
||||
actions.appendChild(startHostButton)
|
||||
actions.appendChild(stopHostButton)
|
||||
}
|
||||
actions.appendChild(installButton)
|
||||
actions.appendChild(selectButton)
|
||||
|
||||
const removeButton = document.createElement('button')
|
||||
removeButton.className = 'launcherGhostButton'
|
||||
@@ -369,6 +342,9 @@ async function renderLibraryView(){
|
||||
if(typeof refreshSelectedProfileButton === 'function'){
|
||||
refreshSelectedProfileButton()
|
||||
}
|
||||
if(typeof refreshServerStatus === 'function'){
|
||||
refreshServerStatus(true)
|
||||
}
|
||||
await renderLibraryView()
|
||||
if(typeof refreshInstallView === 'function'){
|
||||
await refreshInstallView()
|
||||
@@ -379,10 +355,6 @@ async function renderLibraryView(){
|
||||
|
||||
card.appendChild(header)
|
||||
card.appendChild(description)
|
||||
card.appendChild(infoBlock)
|
||||
if(fieldGroup.childNodes.length > 0){
|
||||
card.appendChild(fieldGroup)
|
||||
}
|
||||
card.appendChild(actions)
|
||||
libraryList.appendChild(card)
|
||||
}
|
||||
@@ -396,31 +368,10 @@ async function renderLibraryView(){
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('libraryOpenInstallButton').addEventListener('click', async () => {
|
||||
if(typeof refreshInstallView === 'function'){
|
||||
await refreshInstallView()
|
||||
}
|
||||
switchView(getCurrentView(), VIEWS.install)
|
||||
})
|
||||
|
||||
document.getElementById('libraryBackButton').addEventListener('click', () => {
|
||||
switchView(getCurrentView(), VIEWS.landing)
|
||||
})
|
||||
|
||||
document.getElementById('libraryOpenSettingsButton').addEventListener('click', async () => {
|
||||
await prepareSettings()
|
||||
switchView(getCurrentView(), VIEWS.settings)
|
||||
})
|
||||
|
||||
document.getElementById('libraryOpenLaunchButton').addEventListener('click', async () => {
|
||||
const selectedProfile = CatalogManager.getSelectedProfileSync()
|
||||
if(selectedProfile == null){
|
||||
switchView(getCurrentView(), VIEWS.install)
|
||||
return
|
||||
}
|
||||
await activateProfile(selectedProfile, false)
|
||||
})
|
||||
|
||||
setInterval(() => {
|
||||
if(getCurrentView() === VIEWS.library && ServerRuntime.hasRunningProfiles()){
|
||||
renderLibraryView()
|
||||
|
||||
@@ -152,7 +152,7 @@ function formDisabled(v){
|
||||
loginRememberOption.disabled = v
|
||||
}
|
||||
|
||||
let loginViewOnSuccess = VIEWS.library
|
||||
let loginViewOnSuccess = VIEWS.landing
|
||||
let loginViewOnCancel = VIEWS.settings
|
||||
let loginViewCancelHandler
|
||||
|
||||
@@ -198,7 +198,7 @@ loginButton.addEventListener('click', () => {
|
||||
if(loginViewOnSuccess === VIEWS.settings){
|
||||
await prepareSettings()
|
||||
}
|
||||
loginViewOnSuccess = VIEWS.library // Reset this for good measure.
|
||||
loginViewOnSuccess = VIEWS.landing // Reset this for good measure.
|
||||
loginCancelEnabled(false) // Reset this for good measure.
|
||||
loginViewCancelHandler = null // Reset this for good measure.
|
||||
loginUsername.value = ''
|
||||
|
||||
@@ -337,7 +337,7 @@ function fullSettingsSave() {
|
||||
/* Closes the settings view and saves all data. */
|
||||
settingsNavDone.onclick = () => {
|
||||
fullSettingsSave()
|
||||
switchView(getCurrentView(), VIEWS.library)
|
||||
switchView(getCurrentView(), VIEWS.landing)
|
||||
}
|
||||
|
||||
settingsBackButton.onclick = () => {
|
||||
|
||||
@@ -87,11 +87,11 @@ async function showMainUI(data){
|
||||
$(VIEWS.welcome).fadeIn(1000)
|
||||
} else {
|
||||
if(isLoggedIn){
|
||||
currentView = VIEWS.library
|
||||
$(VIEWS.library).fadeIn(1000)
|
||||
currentView = VIEWS.landing
|
||||
$(VIEWS.landing).fadeIn(1000)
|
||||
} else {
|
||||
loginOptionsCancelEnabled(false)
|
||||
loginOptionsViewOnLoginSuccess = VIEWS.library
|
||||
loginOptionsViewOnLoginSuccess = VIEWS.landing
|
||||
loginOptionsViewOnLoginCancel = VIEWS.loginOptions
|
||||
currentView = VIEWS.loginOptions
|
||||
$(VIEWS.loginOptions).fadeIn(1000)
|
||||
@@ -105,10 +105,6 @@ async function showMainUI(data){
|
||||
}, 250)
|
||||
|
||||
}, 750)
|
||||
// Disable tabbing to the news container.
|
||||
initNews().then(() => {
|
||||
$('#newsContainer *').attr('tabindex', '-1')
|
||||
})
|
||||
}
|
||||
|
||||
function showFatalStartupError(){
|
||||
@@ -137,7 +133,6 @@ function showFatalStartupError(){
|
||||
function onDistroRefresh(data){
|
||||
updateSelectedServer(data.getServerById(ConfigManager.getSelectedServer()))
|
||||
refreshServerStatus()
|
||||
initNews()
|
||||
syncModConfigurations(data)
|
||||
ensureJavaSettings(data)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
document.getElementById('welcomeButton').addEventListener('click', e => {
|
||||
loginOptionsCancelEnabled(false) // False by default, be explicit.
|
||||
loginOptionsViewOnLoginSuccess = VIEWS.library
|
||||
loginOptionsViewOnLoginSuccess = VIEWS.landing
|
||||
loginOptionsViewOnLoginCancel = VIEWS.loginOptions
|
||||
switchView(VIEWS.welcome, VIEWS.loginOptions)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user