Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions apps/app-frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1545,8 +1545,10 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
<NavButton
v-tooltip.right="formatMessage(commonMessages.discoverContentLabel)"
to="/browse/modpack"
:is-primary="() => route.path.startsWith('/browse') && !route.query.i"
:is-subpage="(route) => route.path.startsWith('/project') && !route.query.i"
:is-primary="() => route.path.startsWith('/browse') && !route.query.i && !route.query.sid"
:is-subpage="
(route) => route.path.startsWith('/project') && !route.query.i && !route.query.sid
"
>
<CompassIcon />
</NavButton>
Expand All @@ -1570,7 +1572,11 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
v-tooltip.right="formatMessage(messages.modrinthHosting)"
to="/hosting/manage"
:is-primary="(r) => r.path === '/hosting/manage' || r.path === '/hosting/manage/'"
:is-subpage="(r) => r.path.startsWith('/hosting/manage/') && r.path !== '/hosting/manage/'"
:is-subpage="
(r) =>
(r.path.startsWith('/hosting/manage/') && r.path !== '/hosting/manage/') ||
((r.path.startsWith('/browse') || r.path.startsWith('/project')) && r.query.sid)
"
>
<ServerStackIcon />
</NavButton>
Expand Down
98 changes: 56 additions & 42 deletions apps/app-frontend/src/pages/Browse.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,7 @@ import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
import { get as getSettings, set as setSettings } from '@/helpers/settings.ts'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import { get_instance_worlds } from '@/helpers/worlds'
import {
type BreadcrumbDefinition,
useBreadcrumb,
useRootBreadcrumb,
} from '@/providers/breadcrumbs'
import { type BreadcrumbDefinition, injectBreadcrumbManager } from '@/providers/breadcrumbs'
import { injectContentInstall } from '@/providers/content-install'
import { injectServerInstall } from '@/providers/server-install'
import {
Expand Down Expand Up @@ -179,45 +175,46 @@ const hiddenInstanceProjectIds = ref<Set<string>>(new Set())
const hiddenInstanceProjectIdsInitialized = ref(false)
const isServerInstance = ref(false)

const instanceBreadcrumb = route.query.i
? useBreadcrumb({
slot: 'instance',
id: () => `instance:${String(displayedBrowseRoute.value.query.i ?? '')}`,
label: () => instance.value?.name ?? formatMessage(commonMessages.loadingLabel),
visual: () => ({
type: 'image',
src: instance.value?.icon_path ? convertFileSrc(instance.value.icon_path) : undefined,
alt: instance.value?.name,
tintBy: String(displayedBrowseRoute.value.query.i ?? ''),
}),
to: () => {
const instancePath = `/instance/${encodeURIComponent(
String(displayedBrowseRoute.value.query.i ?? ''),
)}`
return displayedBrowseRoute.value.query.from === 'worlds'
? `${instancePath}/worlds`
: instancePath
},
})
: undefined
const breadcrumbManager = injectBreadcrumbManager()
const instanceBreadcrumbDefinition = {
slot: 'instance',
id: () => `instance:${String(displayedBrowseRoute.value.query.i ?? '')}`,
label: () => instance.value?.name ?? formatMessage(commonMessages.loadingLabel),
visual: () => ({
type: 'image' as const,
src: instance.value?.icon_path ? convertFileSrc(instance.value.icon_path) : undefined,
alt: instance.value?.name,
tintBy: String(displayedBrowseRoute.value.query.i ?? ''),
}),
to: () => {
const instancePath = `/instance/${encodeURIComponent(
String(displayedBrowseRoute.value.query.i ?? ''),
)}`
return displayedBrowseRoute.value.query.from === 'worlds'
? `${instancePath}/worlds`
: instancePath
},
} satisfies BreadcrumbDefinition
const serversBreadcrumbDefinition = {
slot: 'root',
id: 'servers',
label: () => formatMessage(commonMessages.serversLabel),
to: '/hosting/manage/',
visual: { type: 'icon', component: ServerStackIcon },
} satisfies BreadcrumbDefinition
const serverBreadcrumbTo = ref(serverBackUrl.value)
watch(serverBackUrl, (value) => {
if (route.path.startsWith('/browse/')) {
serverBreadcrumbTo.value = value
}
})
const serverBreadcrumb =
!instanceBreadcrumb && serverIdQuery.value
? useBreadcrumb({
slot: 'server',
id: () => `server:${String(displayedBrowseRoute.value.query.sid ?? '')}`,
label: () =>
serverContextServerData.value?.name ?? formatMessage(commonMessages.loadingLabel),
visual: { type: 'icon', component: ServerStackIcon },
to: serverBreadcrumbTo,
})
: undefined
const breadcrumbParent = instanceBreadcrumb ?? serverBreadcrumb
const serverBreadcrumbDefinition = {
slot: 'server',
id: () => `server:${String(displayedBrowseRoute.value.query.sid ?? '')}`,
label: () => serverContextServerData.value?.name ?? formatMessage(commonMessages.loadingLabel),
visual: { type: 'icon', component: ServerStackIcon },
to: serverBreadcrumbTo,
} satisfies BreadcrumbDefinition
const breadcrumbDefinition = {
slot: 'browse',
id: () =>
Expand All @@ -230,9 +227,27 @@ const breadcrumbDefinition = {
to: () => displayedBrowseRoute.value.fullPath,
visual: { type: 'icon', component: CompassIcon },
} satisfies BreadcrumbDefinition
const browseBreadcrumb = breadcrumbParent
? useBreadcrumb(breadcrumbDefinition, { parent: breadcrumbParent })
: useRootBreadcrumb(breadcrumbDefinition)

function syncBreadcrumbs() {
if (displayedBrowseRoute.value.query.i) {
const instanceBreadcrumb = breadcrumbManager.reset(instanceBreadcrumbDefinition)
breadcrumbManager.push(breadcrumbDefinition, { parent: instanceBreadcrumb })
return
}

if (displayedBrowseRoute.value.query.sid) {
const serversBreadcrumb = breadcrumbManager.reset(serversBreadcrumbDefinition)
const serverBreadcrumb = breadcrumbManager.push(serverBreadcrumbDefinition, {
parent: serversBreadcrumb,
})
breadcrumbManager.push(breadcrumbDefinition, { parent: serverBreadcrumb })
return
}

breadcrumbManager.reset(breadcrumbDefinition)
}

watch(displayedBrowseRoute, syncBreadcrumbs, { immediate: true, flush: 'sync' })

debugLog('fetching tags (categories, loaders, gameVersions)')
const [categories, loaders, availableGameVersions] = await Promise.all([
Expand Down Expand Up @@ -587,7 +602,6 @@ function resetInstanceContext() {
hiddenInstanceProjectIds.value = new Set()
hiddenInstanceProjectIdsInitialized.value = false
isServerInstance.value = false
browseBreadcrumb.reset()
void refreshInstalledProjectIds()
}

Expand Down
42 changes: 39 additions & 3 deletions apps/app-frontend/src/providers/setup/server-install-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,29 @@ export function createServerInstallContent(opts: {
const isSetupServerContext = computed(() => !!serverIdQuery.value && !!serverFlowFrom.value)

const serverContextWorldId = ref<string | null>(worldIdQuery.value)
const serverContextServerData = ref<Archon.Servers.v0.Server | null>(null)

function getCachedServer(serverId: string): Archon.Servers.v0.Server | null {
return (
queryClient.getQueryData<Archon.Servers.v0.Server>(['servers', 'detail', serverId]) ??
queryClient
.getQueryData<Archon.Servers.v0.ServerGetResponse>(['servers'])
?.servers.find((server) => server.server_id === serverId) ??
null
)
}

async function ensureServer(serverId: string): Promise<Archon.Servers.v0.Server> {
return queryClient.ensureQueryData({
queryKey: ['servers', 'detail', serverId],
queryFn: () => client.archon.servers_v0.get(serverId),
staleTime: 30_000,
})
}

const initialServerId = serverIdQuery.value
const serverContextServerData = ref<Archon.Servers.v0.Server | null>(
initialServerId ? getCachedServer(initialServerId) : null,
)
const serverContentProjectIds = ref<Set<string>>(new Set())
const serverContentInstallKeys = ref<Set<string>>(new Set())
const queuedServerInstalls = ref<Map<string, BrowseInstallPlan<InstallableSearchResult>>>(
Expand Down Expand Up @@ -277,6 +299,9 @@ export function createServerInstallContent(opts: {
async function refreshServerInstalledContent(serverId: string, worldId: string) {
try {
const content = await client.archon.content_v1.getAddons(serverId, worldId)
if (serverIdQuery.value !== serverId || effectiveServerWorldId.value !== worldId) {
return
}
const ids = new Set(
(content.addons ?? [])
.map((addon) => addon.project_id)
Expand All @@ -297,14 +322,20 @@ export function createServerInstallContent(opts: {
if (!sid) return

try {
serverContextServerData.value = await client.archon.servers_v0.get(sid)
const server = await ensureServer(sid)
if (serverIdQuery.value === sid) {
serverContextServerData.value = server
}
} catch (err) {
handleError(err as Error)
}

if (serverIdQuery.value !== sid) return

let resolvedWorldId = effectiveServerWorldId.value
if (!resolvedWorldId) {
resolvedWorldId = await resolveServerContextWorldId(sid)
if (serverIdQuery.value !== sid) return
if (resolvedWorldId) {
serverContextWorldId.value = resolvedWorldId
}
Expand All @@ -319,6 +350,7 @@ export function createServerInstallContent(opts: {
function watchServerContextChanges() {
watch([serverIdQuery, effectiveServerWorldId], async ([sid, wid], [prevSid, prevWid]) => {
if (!sid) {
serverContextWorldId.value = null
serverContextServerData.value = null
serverContentProjectIds.value = new Set()
serverContentInstallKeys.value = new Set()
Expand All @@ -330,8 +362,12 @@ export function createServerInstallContent(opts: {
serverContentProjectIds.value = new Set()
serverContentInstallKeys.value = new Set()
queuedServerInstalls.value = readStoredServerInstallQueue(sid, wid)
serverContextServerData.value = getCachedServer(sid)
try {
serverContextServerData.value = await client.archon.servers_v0.get(sid)
const server = await ensureServer(sid)
if (serverIdQuery.value === sid) {
serverContextServerData.value = server
}
} catch (err) {
handleError(err as Error)
}
Expand Down
Loading