diff --git a/apps/app-frontend/src/pages/Browse.vue b/apps/app-frontend/src/pages/Browse.vue index 78abfdc5cf..feb1e3969e 100644 --- a/apps/app-frontend/src/pages/Browse.vue +++ b/apps/app-frontend/src/pages/Browse.vue @@ -10,7 +10,9 @@ import { } from '@modrinth/assets' import type { BrowseInstallContentType, CardAction, ProjectType, Tags } from '@modrinth/ui' import { + BrowseInstallHeader, BrowsePageLayout, + BrowseSidebar, commonMessages, CreationFlowModal, defineMessages, @@ -21,8 +23,10 @@ import { preferencesDiffer, provideBrowseManager, requestInstall, + SelectedProjectsFloatingBar, useBrowseSearch, useDebugLogger, + useStickyObserver, useVIntl, } from '@modrinth/ui' import { useQueryClient } from '@tanstack/vue-query' @@ -79,6 +83,7 @@ const { isSetupServerContext, effectiveServerWorldId, serverContextServerData, + serverContextWorldName, serverContentProjectIds, queuedServerInstallProjectIds, queuedServerInstallCount, @@ -412,6 +417,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: @@ -565,8 +574,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, @@ -606,6 +616,11 @@ const installContext = computed(() => { } return null }) +const stickyInstallHeaderRef = ref(null) +const { isStuck: isInstallHeaderStuck } = useStickyObserver( + stickyInstallHeaderRef, + 'BrowseInstallHeader', +) const installingProjectIds = ref>(new Set()) @@ -1092,6 +1107,17 @@ provideBrowseManager({ diff --git a/packages/ui/src/layouts/shared/browse-tab/types.ts b/packages/ui/src/layouts/shared/browse-tab/types.ts index 3caeb04502..b40411f048 100644 --- a/packages/ui/src/layouts/shared/browse-tab/types.ts +++ b/packages/ui/src/layouts/shared/browse-tab/types.ts @@ -21,6 +21,7 @@ export interface BrowseSelectedProject { export interface BrowseInstallContext { name: string loader: string + loaderVersion?: string | null gameVersion: string serverId?: string | null upstream?: { project_id?: string | null } | null diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/[id]/index.vue b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/index.vue index fedf0f356e..952ec9a580 100644 --- a/packages/ui/src/layouts/wrapped/hosting/manage/[id]/index.vue +++ b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/index.vue @@ -406,6 +406,14 @@ interface Tab { href: string icon?: object subpages?: string[] + prompt?: { + title: string + description: string + dismissLabel?: string + shown?: boolean + placement?: string + onDismiss?: () => void + } } const props = withDefaults( @@ -478,6 +486,21 @@ const settingsHintMessages = defineMessages({ }, }) +const instancesHintMessages = defineMessages({ + title: { + id: 'servers.manage.instances-hint.title', + defaultMessage: 'New: Server Instances!', + }, + description: { + id: 'servers.manage.instances-hint.description', + defaultMessage: 'Your server state has been converted into an instance.', + }, + dismiss: { + id: 'servers.manage.instances-hint.dismiss', + defaultMessage: "Don't show again", + }, +}) + const messages = defineMessages({ serverPreparingTitle: { id: 'servers.manage.status.preparing.title', @@ -723,6 +746,14 @@ function dismissSettingsHint() { settingsHintDismissed.value = true } +const INSTANCES_HINT_KEY = 'server-panel-instances-hint-dismissed' +const instancesHintDismissed = useStorage(INSTANCES_HINT_KEY, false) +const showInstancesHint = ref(!instancesHintDismissed.value) +function dismissInstancesHint() { + showInstancesHint.value = false + instancesHintDismissed.value = true +} + const serverSettingsModal = ref | null>(null) const confirmLeaveModal = ref>() @@ -1043,6 +1074,14 @@ const navLinks = computed(() => [ href: `/hosting/manage/${encodeURIComponent(props.serverId)}/instances`, icon: GlobeIcon, subpages: [], + prompt: { + title: formatMessage(instancesHintMessages.title), + description: formatMessage(instancesHintMessages.description), + dismissLabel: formatMessage(instancesHintMessages.dismiss), + shown: showInstancesHint.value, + placement: 'bottom', + onDismiss: dismissInstancesHint, + }, }, { label: 'Access', diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/[id]/instances/[instance-id]/index.vue b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/instances/[instance-id]/index.vue index 42a25dbd1e..c4fdc437fb 100644 --- a/packages/ui/src/layouts/wrapped/hosting/manage/[id]/instances/[instance-id]/index.vue +++ b/packages/ui/src/layouts/wrapped/hosting/manage/[id]/instances/[instance-id]/index.vue @@ -9,19 +9,8 @@ :back-href="instancesPath" :back-label="formatMessage(messages.allInstances)" :fallback-name="formatMessage(messages.worldFallbackName)" - > - - + :actions="headerActions" + /> @@ -31,14 +20,24 @@