Skip to content
Draft
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
14 changes: 2 additions & 12 deletions apps/frontend/src/composables/servers/modrinth-servers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { AbstractWebNotificationManager } from '@modrinth/ui'
import type { JWTAuth, ModuleError, ModuleName } from '@modrinth/utils'
import { ModrinthServerError } from '@modrinth/utils'

import { ContentModule, GeneralModule, NetworkModule, StartupModule } from './modules/index.ts'
import { GeneralModule, NetworkModule, StartupModule } from './modules/index.ts'
import { useServersFetch } from './servers-fetch.ts'

export function handleServersError(err: any, notifications: AbstractWebNotificationManager) {
Expand All @@ -27,15 +27,13 @@ export class ModrinthServer {
private errors: Partial<Record<ModuleName, ModuleError>> = {}

readonly general: GeneralModule
readonly content: ContentModule
readonly network: NetworkModule
readonly startup: StartupModule

constructor(serverId: string) {
this.serverId = serverId

this.general = new GeneralModule(this)
this.content = new ContentModule(this)
this.network = new NetworkModule(this)
this.startup = new StartupModule(this)
}
Expand Down Expand Up @@ -209,7 +207,7 @@ export class ModrinthServer {
},
): Promise<void> {
const modulesToRefresh =
modules.length > 0 ? modules : (['general', 'content', 'network', 'startup'] as ModuleName[])
modules.length > 0 ? modules : (['general', 'network', 'startup'] as ModuleName[])

for (const module of modulesToRefresh) {
this.errors[module] = undefined
Expand Down Expand Up @@ -238,9 +236,6 @@ export class ModrinthServer {
}
break
}
case 'content':
await this.content.fetch()
break
case 'network':
await this.network.fetch()
break
Expand All @@ -250,11 +245,6 @@ export class ModrinthServer {
}
} catch (error) {
if (error instanceof ModrinthServerError) {
if (error.statusCode === 404 && module === 'content') {
console.debug(`Optional ${module} resource not found:`, error.message)
continue
}

if (error.statusCode && error.statusCode >= 500) {
console.debug(`Temporary ${module} unavailable:`, error.message)
continue
Expand Down
37 changes: 0 additions & 37 deletions apps/frontend/src/composables/servers/modules/content.ts

This file was deleted.

1 change: 0 additions & 1 deletion apps/frontend/src/composables/servers/modules/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export * from './backups.ts'
export * from './base.ts'
export * from './content.ts'
export * from './general.ts'
export * from './network.ts'
export * from './startup.ts'
Expand Down
88 changes: 71 additions & 17 deletions apps/frontend/src/pages/discover/[type]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,25 @@ import {
SearchIcon,
XIcon,
} from '@modrinth/assets'
import { defineMessages, useVIntl } from '@modrinth/ui'
import {
Avatar,
Button,
ButtonStyled,
Checkbox,
defineMessages,
DropdownSelect,
injectModrinthClient,
injectNotificationManager,
NewProjectCard,
Pagination,
SearchFilterControl,
SearchSidebarFilter,
type SortType,
useSearch,
useVIntl,
} from '@modrinth/ui'
import { capitalizeString, cycleValue, type Mod as InstallableMod } from '@modrinth/utils'
import { capitalizeString, cycleValue } from '@modrinth/utils'
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
import { useThrottleFn } from '@vueuse/core'
import { computed, type Reactive, watch } from 'vue'

Expand All @@ -40,10 +43,13 @@ import type { DisplayLocation, DisplayMode } from '~/plugins/cosmetics.ts'

const { formatMessage } = useVIntl()

const client = injectModrinthClient()
const queryClient = useQueryClient()

const filtersMenuOpen = ref(false)

const route = useNativeRoute()
const router = useNativeRouter()
const route = useRoute()
const router = useRouter()

const cosmetics = useCosmetics()
const tags = useGeneratedState()
Expand Down Expand Up @@ -72,6 +78,48 @@ const server = ref<Reactive<ModrinthServer>>()
const serverHideInstalled = ref(false)
const eraseDataOnInstall = ref(false)

const currentServerId = computed(() => queryAsString(route.query.sid) || null)

// TanStack Query for server content list
const contentQueryKey = computed(() => ['content', 'list', currentServerId.value ?? ''] as const)
const { data: serverContentData, error: serverContentError } = useQuery({
queryKey: contentQueryKey,
queryFn: () => client.archon.content_v0.list(currentServerId.value!),
enabled: computed(() => !!currentServerId.value),
})

// Watch for errors and notify user
watch(serverContentError, (error) => {
if (error) {
console.error('Failed to load server content:', error)
handleError(error)
}
})

// Install content mutation
const installContentMutation = useMutation({
mutationFn: ({
serverId,
type,
projectId,
versionId,
}: {
serverId: string
type: 'mod' | 'plugin'
projectId: string
versionId: string
}) =>
client.archon.content_v0.install(serverId, {
rinth_ids: { project_id: projectId, version_id: versionId },
install_as: type,
}),
onSuccess: () => {
if (currentServerId.value) {
queryClient.invalidateQueries({ queryKey: ['content', 'list', currentServerId.value] })
}
},
})

const PERSISTENT_QUERY_PARAMS = ['sid', 'shi']

async function updateServerContext() {
Expand All @@ -89,7 +137,7 @@ async function updateServerContext() {
}

if (!server.value || server.value.serverId !== serverId) {
server.value = await useModrinthServers(serverId, ['general', 'content'])
server.value = await useModrinthServers(serverId, ['general'])
}

if (route.query.shi && projectType.value?.id !== 'modpack' && server.value) {
Expand Down Expand Up @@ -147,10 +195,10 @@ const serverFilters = computed(() => {
})
}

if (serverHideInstalled.value) {
const installedMods = server.value.content?.data
.filter((x: InstallableMod) => x.project_id)
.map((x: InstallableMod) => x.project_id)
if (serverHideInstalled.value && serverContentData.value) {
const installedMods = serverContentData.value
.filter((x) => x.project_id)
.map((x) => x.project_id)
.filter((id): id is string => id !== undefined)

installedMods
Expand Down Expand Up @@ -251,12 +299,20 @@ async function serverInstall(project: InstallableSearchResult) {
project.installed = true
navigateTo(`/hosting/manage/${server.value.serverId}/options/loader`)
} else if (projectType.value?.id === 'mod') {
await server.value.content.install('mod', version.project_id, version.id)
await server.value.refresh(['content'])
await installContentMutation.mutateAsync({
serverId: server.value.serverId,
type: 'mod',
projectId: version.project_id,
versionId: version.id,
})
project.installed = true
} else if (projectType.value?.id === 'plugin') {
await server.value.content.install('plugin', version.project_id, version.id)
await server.value.refresh(['content'])
await installContentMutation.mutateAsync({
serverId: server.value.serverId,
type: 'plugin',
projectId: version.project_id,
versionId: version.id,
})
project.installed = true
}
} catch (e) {
Expand Down Expand Up @@ -643,10 +699,8 @@ useSeoMeta({
<button
v-if="
(result as InstallableSearchResult).installed ||
(server?.content?.data &&
server.content.data.find(
(x: InstallableMod) => x.project_id === result.project_id,
)) ||
(serverContentData &&
serverContentData.find((x) => x.project_id === result.project_id)) ||
server.general?.project?.id === result.project_id
"
disabled
Expand Down
28 changes: 14 additions & 14 deletions apps/frontend/src/pages/hosting/manage/[id]/content.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
<template>
<div class="flex h-full w-full flex-col">
<NuxtPage :route="route" :server="props.server" />
</div>
</template>

<script setup lang="ts">
import type { ModrinthServer } from '~/composables/servers/modrinth-servers.ts'

const route = useNativeRoute()
import { injectModrinthServerContext, ServersManageContentPage } from '@modrinth/ui'

const props = defineProps<{
server: ModrinthServer
}>()
import { useGeneratedState } from '~/composables/generated'

const data = computed(() => props.server.general)
const { server } = injectModrinthServerContext()
const generatedState = useGeneratedState()

useHead({
title: `Content - ${data.value?.name ?? 'Server'} - Modrinth`,
title: `Content - ${server.value?.name ?? 'Server'} - Modrinth`,
})

const tags = computed(() => ({
gameVersions: generatedState.value.gameVersions,
loaders: generatedState.value.loaders,
}))
</script>

<template>
<ServersManageContentPage :tags="tags" />
</template>
Loading