You've already forked AstralRinth
fix: update browse page when installing dependencies (#6414)
* fix: update browse page when installing dependencies * fix: lint * fix: vers page * fix: vers page link query persistence
This commit is contained in:
@@ -29,7 +29,7 @@ import {
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import type { Ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import type { LocationQuery } from 'vue-router'
|
||||
import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
get_search_results_v3,
|
||||
get_version_many,
|
||||
} from '@/helpers/cache.js'
|
||||
import { profile_listener } from '@/helpers/events.js'
|
||||
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
|
||||
import {
|
||||
get as getInstance,
|
||||
@@ -181,6 +182,28 @@ watchServerContextChanges()
|
||||
|
||||
await initInstanceContext()
|
||||
|
||||
async function refreshInstalledProjectIds() {
|
||||
if (!route.query.i) return
|
||||
|
||||
if (route.query.from === 'worlds') {
|
||||
const worlds = await get_profile_worlds(route.query.i as string).catch(handleError)
|
||||
if (!worlds) return
|
||||
|
||||
const serverProjectIds = worlds
|
||||
.filter((w) => w.type === 'server' && 'project_id' in w && w.project_id)
|
||||
.map((w) => (w as { project_id: string }).project_id)
|
||||
debugLog('installedServerProjectIds loaded', { count: serverProjectIds.length })
|
||||
installedProjectIds.value = serverProjectIds
|
||||
return
|
||||
}
|
||||
|
||||
const ids = await getInstalledProjectIds(route.query.i as string).catch(handleError)
|
||||
if (!ids) return
|
||||
|
||||
debugLog('installedProjectIds loaded', { count: ids.length })
|
||||
installedProjectIds.value = ids
|
||||
}
|
||||
|
||||
async function initInstanceContext() {
|
||||
debugLog('initInstanceContext', {
|
||||
queryI: route.query.i,
|
||||
@@ -199,24 +222,7 @@ async function initInstanceContext() {
|
||||
gameVersion: instance.value?.game_version,
|
||||
})
|
||||
|
||||
if (route.query.from === 'worlds') {
|
||||
get_profile_worlds(route.query.i as string)
|
||||
.then((worlds) => {
|
||||
const serverProjectIds = worlds
|
||||
.filter((w) => w.type === 'server' && 'project_id' in w && w.project_id)
|
||||
.map((w) => (w as { project_id: string }).project_id)
|
||||
debugLog('installedServerProjectIds loaded', { count: serverProjectIds.length })
|
||||
installedProjectIds.value = serverProjectIds
|
||||
})
|
||||
.catch(handleError)
|
||||
} else {
|
||||
getInstalledProjectIds(route.query.i as string)
|
||||
.then((ids) => {
|
||||
debugLog('installedProjectIds loaded', { count: ids?.length })
|
||||
installedProjectIds.value = ids
|
||||
})
|
||||
.catch(handleError)
|
||||
}
|
||||
await refreshInstalledProjectIds()
|
||||
|
||||
if (instance.value?.linked_data?.project_id) {
|
||||
debugLog('checking linked project for server status', instance.value.linked_data.project_id)
|
||||
@@ -805,10 +811,10 @@ function getCardActions(
|
||||
selectedInstall.versionId,
|
||||
instance.value ? instance.value.path : null,
|
||||
'SearchCard',
|
||||
(versionId) => {
|
||||
(versionId, installedProjectIds) => {
|
||||
setProjectInstalling(projectResult.project_id, false)
|
||||
if (versionId) {
|
||||
onSearchResultInstalled(projectResult.project_id)
|
||||
onSearchResultsInstalled(installedProjectIds ?? [projectResult.project_id])
|
||||
}
|
||||
},
|
||||
(profile) => {
|
||||
@@ -834,7 +840,19 @@ function onSearchResultInstalled(id: string) {
|
||||
markServerProjectInstalled(id)
|
||||
return
|
||||
}
|
||||
newlyInstalled.value.push(id)
|
||||
if (!newlyInstalled.value.includes(id)) {
|
||||
newlyInstalled.value = [...newlyInstalled.value, id]
|
||||
}
|
||||
}
|
||||
|
||||
function onSearchResultsInstalled(ids: string[]) {
|
||||
if (isServerContext.value) {
|
||||
for (const id of ids) {
|
||||
markServerProjectInstalled(id)
|
||||
}
|
||||
return
|
||||
}
|
||||
newlyInstalled.value = Array.from(new Set([...newlyInstalled.value, ...ids]))
|
||||
}
|
||||
|
||||
async function search(requestParams: string) {
|
||||
@@ -966,6 +984,38 @@ if (instance.value?.game_version) {
|
||||
|
||||
await searchState.refreshSearch()
|
||||
|
||||
type UnlistenFn = () => void
|
||||
|
||||
let isUnmounted = false
|
||||
let unlistenProfiles: UnlistenFn | null = null
|
||||
|
||||
onMounted(() => {
|
||||
profile_listener(async (event: { event: string; profile_path_id: string }) => {
|
||||
if (
|
||||
instance.value &&
|
||||
event.profile_path_id === instance.value.path &&
|
||||
event.event === 'synced'
|
||||
) {
|
||||
await refreshInstalledProjectIds()
|
||||
await searchState.refreshSearch()
|
||||
}
|
||||
})
|
||||
.then((unlisten) => {
|
||||
if (isUnmounted) {
|
||||
unlisten()
|
||||
return
|
||||
}
|
||||
|
||||
unlistenProfiles = unlisten
|
||||
})
|
||||
.catch(handleError)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
isUnmounted = true
|
||||
unlistenProfiles?.()
|
||||
})
|
||||
|
||||
function getProjectBrowseQuery() {
|
||||
if (!installContext.value) return undefined
|
||||
return {
|
||||
|
||||
@@ -406,6 +406,20 @@ function buildProjectHref(path, extraQuery = {}) {
|
||||
return qs ? `${path}?${qs}` : path
|
||||
}
|
||||
|
||||
function buildBrowseHref(path) {
|
||||
const params = new URLSearchParams()
|
||||
for (const [key, val] of Object.entries(route.query)) {
|
||||
if (key === 'b') continue
|
||||
if (Array.isArray(val)) {
|
||||
for (const v of val) params.append(key, v)
|
||||
} else if (val) {
|
||||
params.append(key, String(val))
|
||||
}
|
||||
}
|
||||
const qs = params.toString()
|
||||
return qs ? `${path}?${qs}` : path
|
||||
}
|
||||
|
||||
const projectDescriptionHref = computed(() => buildProjectHref(`/project/${route.params.id}`))
|
||||
const versionsHref = computed(() =>
|
||||
buildProjectHref(`/project/${route.params.id}/versions`, instanceFilters.value),
|
||||
@@ -416,7 +430,7 @@ const projectBrowseBackUrl = computed(() => {
|
||||
const browsePath = route.query.b
|
||||
if (typeof browsePath === 'string' && browsePath.startsWith('/browse/')) return browsePath
|
||||
const type = data.value?.project_type ? `${data.value.project_type}` : 'mod'
|
||||
return `/browse/${type}`
|
||||
return buildBrowseHref(`/browse/${type}`)
|
||||
})
|
||||
|
||||
const projectInstallContext = computed(() => {
|
||||
@@ -725,10 +739,11 @@ async function install(version) {
|
||||
version,
|
||||
instance.value ? instance.value.path : null,
|
||||
'ProjectPage',
|
||||
(version) => {
|
||||
(version, installedProjectIds) => {
|
||||
installing.value = false
|
||||
|
||||
if (instance.value && version) {
|
||||
const installedIds = installedProjectIds ?? [data.value.id]
|
||||
if (instance.value && version && installedIds.includes(data.value.id)) {
|
||||
installed.value = true
|
||||
installedVersion.value = version
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:current-title="version.name"
|
||||
:link-stack="[
|
||||
{
|
||||
href: `/project/${route.params.id}/versions`,
|
||||
href: buildProjectHref(`/project/${route.params.id}/versions`),
|
||||
label: 'Versions',
|
||||
},
|
||||
]"
|
||||
@@ -249,6 +249,19 @@ const author = computed(() =>
|
||||
|
||||
const displayDependencies = ref({})
|
||||
|
||||
function buildProjectHref(path) {
|
||||
const params = new URLSearchParams()
|
||||
for (const [key, val] of Object.entries(route.query)) {
|
||||
if (Array.isArray(val)) {
|
||||
for (const v of val) params.append(key, v)
|
||||
} else if (val) {
|
||||
params.append(key, String(val))
|
||||
}
|
||||
}
|
||||
const qs = params.toString()
|
||||
return qs ? `${path}?${qs}` : path
|
||||
}
|
||||
|
||||
async function refreshDisplayDependencies() {
|
||||
const projectIds = new Set()
|
||||
const versionIds = new Set()
|
||||
@@ -282,7 +295,7 @@ async function refreshDisplayDependencies() {
|
||||
icon: project?.icon_url,
|
||||
title: project?.title || project?.name,
|
||||
subtitle: `Version ${version.version_number} is ${dependency.dependency_type}`,
|
||||
link: `/project/${project.slug}/version/${version.id}`,
|
||||
link: buildProjectHref(`/project/${project.slug}/version/${version.id}`),
|
||||
}
|
||||
} else {
|
||||
const project = dependencies.projects.find((obj) => obj.id === dependency.project_id)
|
||||
@@ -292,7 +305,7 @@ async function refreshDisplayDependencies() {
|
||||
icon: project?.icon_url,
|
||||
title: project?.title || project?.name,
|
||||
subtitle: `${dependency.dependency_type}`,
|
||||
link: `/project/${project.slug}`,
|
||||
link: buildProjectHref(`/project/${project.slug}`),
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:game-versions="gameVersions"
|
||||
:versions="versions"
|
||||
:project="project"
|
||||
:version-link="(version) => `/project/${project.id}/version/${version.id}`"
|
||||
:version-link="(version) => buildProjectHref(`/project/${project.id}/version/${version.id}`)"
|
||||
>
|
||||
<template #actions="{ version }">
|
||||
<ButtonStyled circular type="transparent">
|
||||
@@ -73,6 +73,7 @@ import {
|
||||
ProjectPageVersions,
|
||||
} from '@modrinth/ui'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
import { SwapIcon } from '@/assets/icons/index.js'
|
||||
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
||||
@@ -109,6 +110,20 @@ defineProps({
|
||||
})
|
||||
|
||||
const { handleError } = injectNotificationManager()
|
||||
const route = useRoute()
|
||||
|
||||
function buildProjectHref(path) {
|
||||
const params = new URLSearchParams()
|
||||
for (const [key, val] of Object.entries(route.query)) {
|
||||
if (Array.isArray(val)) {
|
||||
for (const v of val) params.append(key, v)
|
||||
} else if (val) {
|
||||
params.append(key, String(val))
|
||||
}
|
||||
}
|
||||
const qs = params.toString()
|
||||
return qs ? `${path}?${qs}` : path
|
||||
}
|
||||
|
||||
const [loaders, gameVersions] = await Promise.all([
|
||||
get_loaders().catch(handleError).then(ref),
|
||||
|
||||
@@ -42,6 +42,8 @@ interface ModpackAlreadyInstalledModalRef {
|
||||
show: (instanceName: string, instancePath: string) => void
|
||||
}
|
||||
|
||||
export type ContentInstallCallback = (versionId?: string, installedProjectIds?: string[]) => void
|
||||
|
||||
const LOADER_ORDER = ['vanilla', 'fabric', 'quilt', 'neoforge', 'forge']
|
||||
const SUPPORTED_LOADERS: Set<string> = new Set(['vanilla', 'forge', 'fabric', 'quilt', 'neoforge'])
|
||||
const VANILLA_COMPATIBLE_LOADERS: Set<string> = new Set(['minecraft', 'datapack'])
|
||||
@@ -102,7 +104,7 @@ export interface ContentInstallContext {
|
||||
versionId?: string | null,
|
||||
instancePath?: string | null,
|
||||
source?: string,
|
||||
callback?: (versionId?: string) => void,
|
||||
callback?: ContentInstallCallback,
|
||||
createInstanceCallback?: (profile: string) => void,
|
||||
hints?: { preferredLoader?: string; preferredGameVersion?: string; showProjectInfo?: boolean },
|
||||
) => Promise<void>
|
||||
@@ -256,25 +258,25 @@ export function createContentInstall(opts: {
|
||||
let incompatibilityWarningModalRef: ModalRef | null = null
|
||||
let currentProject: Labrinth.Projects.v2.Project | null = null
|
||||
let currentVersions: Labrinth.Versions.v2.Version[] = []
|
||||
let currentCallback: (versionId?: string) => void = () => {}
|
||||
let currentCallback: ContentInstallCallback = () => {}
|
||||
let profileMap: Record<string, GameInstance> = {}
|
||||
let incompatibilityWarningInstance: GameInstance | null = null
|
||||
let incompatibilityWarningProject: Labrinth.Projects.v2.Project | null = null
|
||||
let incompatibilityWarningCallback: (versionId?: string) => void = () => {}
|
||||
let incompatibilityWarningCallback: ContentInstallCallback = () => {}
|
||||
let incompatibilityWarningInstalled = false
|
||||
|
||||
let pendingModpackInstall: {
|
||||
project: Labrinth.Projects.v2.Project
|
||||
version: string
|
||||
source: string
|
||||
callback: (versionId?: string) => void
|
||||
callback: ContentInstallCallback
|
||||
createInstanceCallback: (profile: string) => void
|
||||
} | null = null
|
||||
|
||||
async function showModInstallModal(
|
||||
project: Labrinth.Projects.v2.Project,
|
||||
versions: Labrinth.Versions.v2.Version[],
|
||||
onInstall: (versionId?: string) => void,
|
||||
onInstall: ContentInstallCallback,
|
||||
hints?: { preferredLoader?: string; preferredGameVersion?: string; showProjectInfo?: boolean },
|
||||
) {
|
||||
currentProject = project
|
||||
@@ -440,7 +442,7 @@ export function createContentInstall(opts: {
|
||||
if (versionId && storeInstance) {
|
||||
storeInstance.installed = true
|
||||
}
|
||||
currentCallback(versionId)
|
||||
currentCallback(versionId, versionId && currentProject ? [currentProject.id] : undefined)
|
||||
}
|
||||
await showIncompatibilityWarning(
|
||||
profile,
|
||||
@@ -487,7 +489,7 @@ export function createContentInstall(opts: {
|
||||
title: currentProject!.title,
|
||||
source: 'ProjectInstallModal',
|
||||
})
|
||||
currentCallback(version.id)
|
||||
currentCallback(version.id, installedProjectIds)
|
||||
} catch (err) {
|
||||
if (storeInstance) storeInstance.installing = false
|
||||
opts.handleError(err)
|
||||
@@ -501,7 +503,7 @@ export function createContentInstall(opts: {
|
||||
project: Labrinth.Projects.v2.Project,
|
||||
versions: Labrinth.Versions.v2.Version[],
|
||||
version: Labrinth.Versions.v2.Version,
|
||||
callback: (versionId?: string) => void,
|
||||
callback: ContentInstallCallback,
|
||||
) {
|
||||
incompatibilityWarningInstance = instance
|
||||
incompatibilityWarningProject = project
|
||||
@@ -542,7 +544,7 @@ export function createContentInstall(opts: {
|
||||
|
||||
incompatibilityWarningInstalling.value = false
|
||||
incompatibilityWarningInstalled = true
|
||||
incompatibilityWarningCallback(version.id)
|
||||
incompatibilityWarningCallback(version.id, [incompatibilityWarningProject.id])
|
||||
incompatibilityWarningModalRef?.hide()
|
||||
|
||||
trackEvent('ProjectInstall', {
|
||||
@@ -630,7 +632,7 @@ export function createContentInstall(opts: {
|
||||
versionId?: string | null,
|
||||
instancePath?: string | null,
|
||||
source: string = 'unknown',
|
||||
callback: (versionId?: string) => void = () => {},
|
||||
callback: ContentInstallCallback = () => {},
|
||||
createInstanceCallback: (profile: string) => void = () => {},
|
||||
hints?: { preferredLoader?: string; preferredGameVersion?: string; showProjectInfo?: boolean },
|
||||
) {
|
||||
@@ -714,7 +716,7 @@ export function createContentInstall(opts: {
|
||||
title: project.title,
|
||||
source,
|
||||
})
|
||||
callback(version.id)
|
||||
callback(version.id, installedProjectIds)
|
||||
} finally {
|
||||
removeInstallingItems(instancePath, installedProjectIds)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user