diff --git a/app/assets/css/launcher.css b/app/assets/css/launcher.css index 67891f2..9bdc7dd 100644 --- a/app/assets/css/launcher.css +++ b/app/assets/css/launcher.css @@ -2470,18 +2470,18 @@ input:checked + .toggleSwitchSlider:before { } #landingContainer > #upper > #left { display: inline-flex; - width: 15%; + width: 14%; height: 100%; justify-content: flex-end; } #landingContainer > #upper > #content { display: inline-flex; - width: 63%; + width: 58%; height: 100%; } #landingContainer > #upper > #right { display: inline-flex; - width: 22%; + width: 28%; height: 100%; justify-content: flex-end; } @@ -2505,7 +2505,7 @@ input:checked + .toggleSwitchSlider:before { position: relative; top: 25px; display: inline-flex; - line-height: 24px; + line-height: 28px; left: 50px; } #landingContainer > #lower > #center { @@ -2991,19 +2991,19 @@ input:checked + .toggleSwitchSlider:before { display: flex; flex-direction: column; align-items: flex-end; - gap: 4px; - min-width: 150px; + gap: 6px; + min-width: 210px; } #accountPreviewLabel { - font-size: 11px; + font-size: 13px; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(255, 255, 255, 0.58); } #accountPreviewName { - font-size: 13px; + font-size: 18px; font-weight: 800; color: #ffffff; text-align: right; @@ -3028,11 +3028,11 @@ input:checked + .toggleSwitchSlider:before { /* User profile avatar container. */ #avatarContainer { border-radius: 50%; - border: 2px solid #cad7e1; + border: 3px solid #cad7e1; box-sizing: border-box; background: rgba(1, 2, 1, 0.5); - height: 70px; - width: 70px; + height: 88px; + width: 88px; box-shadow: 0px 0px 10px 0px rgb(0, 0, 0); overflow: hidden; position: relative; @@ -3049,16 +3049,16 @@ input:checked + .toggleSwitchSlider:before { } #accountMenu { - min-width: 190px; - padding: 14px; - border-radius: 16px; + min-width: 220px; + padding: 16px; + border-radius: 18px; border: 1px solid rgba(255, 255, 255, 0.08); background: rgba(10, 10, 10, 0.9); backdrop-filter: blur(12px); display: flex; flex-direction: column; align-items: stretch; - gap: 12px; + gap: 14px; box-shadow: 0 18px 40px rgba(0, 0, 0, 0.28); } @@ -3067,7 +3067,7 @@ input:checked + .toggleSwitchSlider:before { } #accountMenuName { - font-size: 13px; + font-size: 16px; font-weight: 800; letter-spacing: 0.04em; color: #ffffff; @@ -3083,9 +3083,9 @@ input:checked + .toggleSwitchSlider:before { position: relative; display: flex; flex-direction: column; - margin-top: 25px; - height: calc(100% - 95px); - width: 70px; + margin-top: 32px; + height: calc(100% - 120px); + width: 82px; align-items: center; } @@ -3100,13 +3100,13 @@ input:checked + .toggleSwitchSlider:before { display: flex; justify-content: center; align-items: center; - height: 27px; + height: 36px; } /* Divider bar between the external and internal icons. */ .mediaDivider { height: 1px; - width: 14px; + width: 18px; background: rgb(255, 255, 255); margin: 10px 0px; } @@ -3114,16 +3114,15 @@ input:checked + .toggleSwitchSlider:before { /* Social media icon shared styles. */ .mediaSVG { fill: #ffffff; - height: 12px; transition: 0.25s ease; cursor: pointer; - height: 12px; - width: 25px; + height: 16px; + width: 32px; } .mediaSVG:hover, .mediaURL:focus .mediaSVG, .mediaSVG:active { - height: 20px; + height: 24px; } /* Social media URL shared styles. */ @@ -3148,28 +3147,28 @@ input:checked + .toggleSwitchSlider:before { /* Settings icon colors. */ #settingsSVG { stroke: #ffffff; - height: 15px; + height: 18px; } .mediaButton:hover #settingsSVG, .mediaButton:focus #settingsSVG, .mediaButton:active #settingsSVG { - height: 23px; + height: 27px; } /* Settings tooltip styles. */ #settingsTooltip { visibility: hidden; opacity: 0; - width: 75px; - height: 20px; + width: 88px; + height: 24px; background-color: rgba(0, 0, 0, 0.75); text-align: center; border-radius: 4px; position: absolute; z-index: 1; right: 130%; - font-size: 12px; - line-height: 20px; + font-size: 13px; + line-height: 24px; transition: visibility 0s linear 0.25s, opacity 0.25s ease; } #settingsTooltip::after { @@ -3262,19 +3261,19 @@ input:checked + .toggleSwitchSlider:before { /* Style for a general label on the bottom of the landing view. */ .bot_label { - font-size: 10px; - letter-spacing: 1px; + font-size: 13px; + letter-spacing: 1.2px; font-weight: bold; text-shadow: 0px 0px 0px #bebcbb; } /* Divider used on the bottom of the landing view. */ .bot_divider { - height: 25px; + height: 30px; width: 2px; background: rgba(107, 105, 105, 0.7); - margin-left: 20px; - margin-right: 20px; + margin-left: 22px; + margin-right: 22px; } /* * * @@ -3284,16 +3283,16 @@ input:checked + .toggleSwitchSlider:before { /* Maintains maximum width on the status bar. */ #server_status_wrapper { display: inline-flex; - width: 80px; + width: 180px; } /* Span which displays the player count of the selected server. */ #player_count { color: #949494; - font-size: 9px; + font-size: 14px; font-weight: 900; text-shadow: 0px 0px 20px #949494; - margin-left: 10px; + margin-left: 12px; } /* Wrapper container for the mojang status bar. */ @@ -3305,9 +3304,9 @@ input:checked + .toggleSwitchSlider:before { /* Icon which displays the status of the mojang services. */ #mojang_status_icon { - font-size: 30px; + font-size: 34px; color: #848484; - margin-left: 15px; + margin-left: 16px; font-family: 'sans-serif'; } @@ -3316,7 +3315,7 @@ input:checked + .toggleSwitchSlider:before { position: absolute; visibility: hidden; opacity: 0; - width: 145px; + width: 175px; min-height: 150px; background-color: rgba(0, 0, 0, 0.75); color: #fff; @@ -3324,7 +3323,7 @@ input:checked + .toggleSwitchSlider:before { padding: 5px 10px; z-index: 1; font-family: 'Pretendard Medium'; - font-size: 12px; + font-size: 13px; transition: visibility 0s linear 0.25s, opacity 0.25s ease; bottom: calc(100% + 15px); transform: translateX(-50%); @@ -3372,7 +3371,7 @@ input:checked + .toggleSwitchSlider:before { /* Non essential service title text. */ #mojangStatusNETitle { - font-size: 10px; + font-size: 11px; padding: 0px 3px; text-align: center; letter-spacing: 1px; @@ -3386,16 +3385,16 @@ input:checked + .toggleSwitchSlider:before { /* Displays the name of the mojang service. */ .mojangStatusName { width: 100%; - font-size: 10px; + font-size: 12px; letter-spacing: 1px; - line-height: 12px; - padding: 6px 0px; + line-height: 14px; + padding: 7px 0px; } /* Displays the status of the mojang service. */ .mojangStatusIcon { margin-right: 10px; - font-size: 18.5px; + font-size: 20px; color: #848484; } @@ -3409,6 +3408,9 @@ input:checked + .toggleSwitchSlider:before { border: none; cursor: pointer; outline: none; + display: inline-flex; + align-items: center; + justify-content: center; } #newsButton:hover #newsButtonText, #newsButton:focus #newsButtonText { @@ -3455,10 +3457,10 @@ input:checked + .toggleSwitchSlider:before { #newsButtonText { color: white; font-weight: 900; - letter-spacing: 2px; + letter-spacing: 2.5px; text-shadow: 0px 0px 0px #bebcbb; - font-size: 11px; - line-height: 30px; + font-size: 16px; + line-height: 36px; display: flex; transition: 0.25s ease; } @@ -3482,7 +3484,7 @@ input:checked + .toggleSwitchSlider:before { font-weight: 900; letter-spacing: 2px; text-shadow: 0px 0px 0px #bebcbb; - font-size: 20px; + font-size: 28px; padding: 0px; transition: 0.25s ease; outline: none; @@ -3518,9 +3520,9 @@ input:checked + .toggleSwitchSlider:before { font-weight: 900; letter-spacing: 1px; text-shadow: 0px 0px 0px #bebcbb; - font-size: 20px; - min-width: 53.21px; - max-width: 53.21px; + font-size: 24px; + min-width: 64px; + max-width: 64px; text-align: right; } @@ -3537,7 +3539,8 @@ input:checked + .toggleSwitchSlider:before { border: none; outline: none; cursor: pointer; - line-height: 24px; + font-size: 15px; + line-height: 30px; padding: 0px; transition: 0.25s ease; } @@ -3552,8 +3555,8 @@ input:checked + .toggleSwitchSlider:before { /* Progress bar styles. */ #launch_progress[value] { - height: 3px; - width: 265px; + height: 4px; + width: 320px; -webkit-appearance: none; } #launch_progress[value]::-webkit-progress-bar { @@ -3565,7 +3568,7 @@ input:checked + .toggleSwitchSlider:before { /* Span which displays information about the status of the launch process. */ #launch_details_text { - font-size: 11px; + font-size: 13px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; @@ -4079,8 +4082,8 @@ input:checked + .toggleSwitchSlider:before { .launcherPageEyebrow { display: inline-block; - margin-bottom: 8px; - font-size: 12px; + margin-bottom: 10px; + font-size: 13px; letter-spacing: 0.16em; text-transform: uppercase; color: rgba(255, 255, 255, 0.65); @@ -4088,14 +4091,15 @@ input:checked + .toggleSwitchSlider:before { .launcherPageTitle { margin: 0; - font-size: 32px; + font-size: 38px; font-weight: 700; color: #ffffff; } .launcherPageSubtitle { - max-width: 680px; - margin: 10px 0 0; + max-width: 760px; + margin: 12px 0 0; + font-size: 16px; line-height: 1.5; color: rgba(255, 255, 255, 0.74); } @@ -4103,12 +4107,12 @@ input:checked + .toggleSwitchSlider:before { .launcherBackButton { display: inline-flex; align-items: center; - gap: 8px; + gap: 10px; padding: 0; border: none; background: none; color: rgba(255, 255, 255, 0.88); - font-size: 13px; + font-size: 16px; font-weight: 700; letter-spacing: 0.04em; cursor: pointer; @@ -4116,7 +4120,7 @@ input:checked + .toggleSwitchSlider:before { .launcherBackButton::before { content: '←'; - font-size: 16px; + font-size: 20px; } .launcherBackButton:hover, @@ -4135,7 +4139,7 @@ input:checked + .toggleSwitchSlider:before { #landingNavButtons { display: flex; align-items: center; - gap: 10px; + gap: 12px; flex-wrap: wrap; } @@ -4154,17 +4158,17 @@ input:checked + .toggleSwitchSlider:before { .launcherListContainer { display: flex; flex-direction: column; - gap: 12px; + gap: 14px; } .launcherListItem { display: flex; align-items: center; justify-content: space-between; - gap: 16px; - padding: 18px 20px; + gap: 20px; + padding: 22px 24px; border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 18px; + border-radius: 20px; background: rgba(15, 15, 15, 0.56); backdrop-filter: blur(10px); box-shadow: 0 18px 50px rgba(0, 0, 0, 0.18); @@ -4200,7 +4204,7 @@ input:checked + .toggleSwitchSlider:before { .launcherListTitle { margin: 0; - font-size: 20px; + font-size: 24px; color: #ffffff; } @@ -4213,6 +4217,7 @@ input:checked + .toggleSwitchSlider:before { .launcherListDescription { margin: 0; + font-size: 15px; line-height: 1.5; color: rgba(255, 255, 255, 0.76); } @@ -4228,11 +4233,11 @@ input:checked + .toggleSwitchSlider:before { .launcherCard { display: flex; flex-direction: column; - gap: 14px; - min-height: 220px; - padding: 18px; + gap: 16px; + min-height: 240px; + padding: 22px; border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 18px; + border-radius: 20px; background: rgba(15, 15, 15, 0.56); backdrop-filter: blur(10px); box-shadow: 0 18px 50px rgba(0, 0, 0, 0.18); @@ -4257,7 +4262,7 @@ input:checked + .toggleSwitchSlider:before { .launcherCardTitle { margin: 0; - font-size: 22px; + font-size: 26px; color: #ffffff; } @@ -4271,10 +4276,10 @@ input:checked + .toggleSwitchSlider:before { display: inline-flex; align-items: center; justify-content: center; - padding: 4px 10px; + padding: 5px 12px; border-radius: 999px; background: rgba(255, 255, 255, 0.12); - font-size: 11px; + font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; color: rgba(255, 255, 255, 0.8); @@ -4283,6 +4288,7 @@ input:checked + .toggleSwitchSlider:before { .launcherCardDescription, .launcherNotice span, .launcherEmptyDescription { + font-size: 15px; line-height: 1.5; color: rgba(255, 255, 255, 0.76); } @@ -4296,7 +4302,7 @@ input:checked + .toggleSwitchSlider:before { .launcherEditorTitle { display: block; - font-size: 18px; + font-size: 22px; font-weight: 700; color: #ffffff; } @@ -4304,17 +4310,19 @@ input:checked + .toggleSwitchSlider:before { .launcherEditorDescription { display: block; margin-top: 6px; + font-size: 15px; line-height: 1.5; color: rgba(255, 255, 255, 0.72); } .launcherDetailBody { box-sizing: border-box; - min-height: 140px; - padding: 14px 16px; + min-height: 180px; + padding: 16px 18px; border-radius: 14px; background: rgba(255, 255, 255, 0.04); color: rgba(255, 255, 255, 0.84); + font-size: 15px; line-height: 1.6; white-space: pre-wrap; } @@ -4322,8 +4330,8 @@ input:checked + .toggleSwitchSlider:before { .launcherInfoBlock { display: flex; flex-direction: column; - gap: 8px; - padding: 12px 14px; + gap: 10px; + padding: 14px 16px; border-radius: 14px; background: rgba(255, 255, 255, 0.04); } @@ -4335,7 +4343,7 @@ input:checked + .toggleSwitchSlider:before { } .launcherInfoLabel { - font-size: 12px; + font-size: 13px; color: rgba(255, 255, 255, 0.58); letter-spacing: 0.05em; text-transform: uppercase; @@ -4343,6 +4351,7 @@ input:checked + .toggleSwitchSlider:before { .launcherInfoValue { text-align: right; + font-size: 15px; color: rgba(255, 255, 255, 0.88); } @@ -4373,7 +4382,7 @@ input:checked + .toggleSwitchSlider:before { } .launcherFieldLabel { - font-size: 12px; + font-size: 13px; color: rgba(255, 255, 255, 0.65); letter-spacing: 0.08em; text-transform: uppercase; @@ -4412,10 +4421,10 @@ input:checked + .toggleSwitchSlider:before { .launcherPrimaryButton, .launcherSecondaryButton, .launcherGhostButton { - padding: 11px 16px; - border-radius: 12px; + padding: 13px 18px; + border-radius: 14px; border: 1px solid transparent; - font-size: 14px; + font-size: 16px; font-weight: 600; cursor: pointer; transition: transform 0.2s ease, opacity 0.2s ease, border-color 0.2s ease, background 0.2s ease; @@ -4456,9 +4465,9 @@ input:checked + .toggleSwitchSlider:before { .launcherNotice { display: flex; flex-direction: column; - gap: 6px; - padding: 18px; - border-radius: 18px; + gap: 8px; + padding: 20px; + border-radius: 20px; background: rgba(8, 8, 8, 0.42); border: 1px solid rgba(255, 255, 255, 0.08); } diff --git a/app/assets/js/scripts/install.js b/app/assets/js/scripts/install.js index 21a9bc3..b1117be 100644 --- a/app/assets/js/scripts/install.js +++ b/app/assets/js/scripts/install.js @@ -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() diff --git a/app/assets/js/scripts/landing.js b/app/assets/js/scripts/landing.js index dd18f03..7d341fe 100644 --- a/app/assets/js/scripts/landing.js +++ b/app/assets/js/scripts/landing.js @@ -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() - } - } } }) diff --git a/app/assets/js/scripts/library.js b/app/assets/js/scripts/library.js index 9a36c3b..129116d 100644 --- a/app/assets/js/scripts/library.js +++ b/app/assets/js/scripts/library.js @@ -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() diff --git a/app/assets/js/scripts/login.js b/app/assets/js/scripts/login.js index 4489cb3..6eef5fa 100644 --- a/app/assets/js/scripts/login.js +++ b/app/assets/js/scripts/login.js @@ -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 = '' diff --git a/app/assets/js/scripts/settings.js b/app/assets/js/scripts/settings.js index f96e7ee..0a018a6 100644 --- a/app/assets/js/scripts/settings.js +++ b/app/assets/js/scripts/settings.js @@ -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 = () => { diff --git a/app/assets/js/scripts/uibinder.js b/app/assets/js/scripts/uibinder.js index eb68be9..323849e 100644 --- a/app/assets/js/scripts/uibinder.js +++ b/app/assets/js/scripts/uibinder.js @@ -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) } diff --git a/app/assets/js/scripts/welcome.js b/app/assets/js/scripts/welcome.js index 2488f1c..1f3df6d 100644 --- a/app/assets/js/scripts/welcome.js +++ b/app/assets/js/scripts/welcome.js @@ -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) }) diff --git a/app/assets/lang/en_US.toml b/app/assets/lang/en_US.toml index 994a32d..79a5a32 100644 --- a/app/assets/lang/en_US.toml +++ b/app/assets/lang/en_US.toml @@ -168,6 +168,14 @@ logoutFailed = "Failed to log out of the selected account. Please try again." noSelection = "Select from Library" loading = "Loading profile.." +[js.landing.profileStatus] +label = "PROFILE" + +[js.landing.mapStatus] +label = "STATUS" +ready = "READY TO RUN" +notReady = "SETUP REQUIRED" + [js.landing.selectedServer] noSelection = "No Server Selected" loading = "Loading.." diff --git a/app/assets/lang/ko_KR.toml b/app/assets/lang/ko_KR.toml index 5951a2d..19f35f0 100644 --- a/app/assets/lang/ko_KR.toml +++ b/app/assets/lang/ko_KR.toml @@ -168,6 +168,14 @@ logoutFailed = "계정을 로그아웃하지 못했습니다. 다시 시도해 noSelection = "라이브러리에서 선택" loading = "프로필 불러오는 중.." +[js.landing.profileStatus] +label = "프로필" + +[js.landing.mapStatus] +label = "실행 상태" +ready = "실행 가능" +notReady = "실행 준비 필요" + [js.landing.selectedServer] noSelection = "선택된 서버 없음" loading = "로딩 중.." diff --git a/app/install.ejs b/app/install.ejs index a78f3be..16b04e0 100644 --- a/app/install.ejs +++ b/app/install.ejs @@ -9,10 +9,6 @@

<%- lang('install.title') %>

<%- lang('install.subtitle') %>

-
- - -
<%- lang('install.notice') %> diff --git a/app/landing.ejs b/app/landing.ejs index 60aa0a7..37a0e31 100644 --- a/app/landing.ejs +++ b/app/landing.ejs @@ -7,10 +7,6 @@
-
- - -
-
+