diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index 1939fa8787..0afdda326b 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -459,6 +459,11 @@ const sidebarOverlayScrollbarsOptions = Object.freeze({ }, }) +router.beforeEach(async (to) => { + const redirect = await resolveLegacyServerInstanceTabRedirect(to) + if (redirect) return redirect +}) + router.beforeEach(() => { suspensePending = false if (routerToken) loading.end(routerToken) @@ -490,6 +495,50 @@ function onSuspensePending() { suspenseToken = loading.begin() } +async function resolveLegacyServerInstanceTabRedirect(to) { + if (!['ServerManageContent', 'ServerManageFiles', 'ServerManageBackups'].includes(to.name)) { + return null + } + + const serverId = getRouteParam(to.params.id) + if (!serverId) return null + + const tabPath = + to.name === 'ServerManageFiles' ? '/files' : to.name === 'ServerManageBackups' ? '/backups' : '' + const instancesPath = `/hosting/manage/${encodeURIComponent(serverId)}/instances` + + try { + const serverFull = await tauriApiClient.archon.servers_v1.get(serverId) + const world = serverFull.worlds.find((item) => item.is_active) ?? serverFull.worlds[0] + if (world) { + return { + path: `${instancesPath}/${encodeURIComponent(world.id)}${tabPath}`, + query: to.query, + hash: to.hash, + replace: true, + } + } + } catch { + return { + path: instancesPath, + query: to.query, + hash: to.hash, + replace: true, + } + } + + return { + path: instancesPath, + query: to.query, + hash: to.hash, + replace: true, + } +} + +function getRouteParam(param) { + return Array.isArray(param) ? param[0] : param +} + function onSuspenseResolve() { if (suspenseToken) { loading.end(suspenseToken) diff --git a/apps/app-frontend/src/locales/en-US/index.json b/apps/app-frontend/src/locales/en-US/index.json index 51757b059d..1bbaae9ccf 100644 --- a/apps/app-frontend/src/locales/en-US/index.json +++ b/apps/app-frontend/src/locales/en-US/index.json @@ -143,6 +143,9 @@ "app.browse.server.installing": { "message": "Installing" }, + "app.browse.server.world-fallback-name": { + "message": "Instance" + }, "app.creation-modal.installing-modpack.description": { "message": "{fileName}" }, diff --git a/apps/app-frontend/src/pages/Browse.vue b/apps/app-frontend/src/pages/Browse.vue index e256d5b024..8c5c24617f 100644 --- a/apps/app-frontend/src/pages/Browse.vue +++ b/apps/app-frontend/src/pages/Browse.vue @@ -10,6 +10,7 @@ import { } from '@modrinth/assets' import type { BrowseInstallContentType, CardAction, ProjectType, Tags } from '@modrinth/ui' import { + BrowseInstallHeader, BrowsePageLayout, BrowseSidebar, commonMessages, @@ -22,8 +23,10 @@ import { preferencesDiffer, provideBrowseManager, requestInstall, + SelectedProjectsFloatingBar, useBrowseSearch, useDebugLogger, + useStickyObserver, useVIntl, } from '@modrinth/ui' import { useQueryClient } from '@tanstack/vue-query' @@ -77,6 +80,7 @@ const { isSetupServerContext, effectiveServerWorldId, serverContextServerData, + serverContextWorldName, serverContentProjectIds, queuedServerInstallProjectIds, queuedServerInstallCount, @@ -405,6 +409,10 @@ const messages = defineMessages({ id: 'app.browse.back-to-instance', defaultMessage: 'Back to instance', }, + worldFallbackName: { + id: 'app.browse.server.world-fallback-name', + defaultMessage: 'Instance', + }, serverInstanceContentWarning: { id: 'app.browse.server-instance-content-warning', defaultMessage: @@ -545,8 +553,9 @@ const selectableProjectTypes = computed(() => { const installContext = computed(() => { if (isServerContext.value && serverContextServerData.value) { return { - name: serverContextServerData.value.name, + name: serverContextWorldName.value ?? formatMessage(messages.worldFallbackName), loader: serverContextServerData.value.loader ?? '', + loaderVersion: serverContextServerData.value.loader_version ?? '', gameVersion: serverContextServerData.value.mc_version ?? '', serverId: serverIdQuery.value, upstream: serverContextServerData.value.upstream, @@ -585,6 +594,11 @@ const installContext = computed(() => { } return null }) +const stickyInstallHeaderRef = ref(null) +const { isStuck: isInstallHeaderStuck } = useStickyObserver( + stickyInstallHeaderRef, + 'BrowseInstallHeader', +) const installingProjectIds = ref>(new Set()) @@ -1031,6 +1045,17 @@ provideBrowseManager({