fix: invalidate tanstack caches on user auth (#5341)
* fix: invalidate tanstack caches on user auth * refactor: clean up invalidate flow * fix: lint
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import type { AbstractModrinthClient } from '@modrinth/api-client'
|
import type { AbstractModrinthClient } from '@modrinth/api-client'
|
||||||
|
|
||||||
const STALE_TIME = 1000 * 60 * 5 // 5 minutes
|
export const STALE_TIME = 1000 * 60 * 5 // 5 minutes
|
||||||
|
export const STALE_TIME_LONG = 1000 * 60 * 10 // 10 minutes
|
||||||
|
|
||||||
export const projectQueryOptions = {
|
export const projectQueryOptions = {
|
||||||
v2: (projectId: string, client: AbstractModrinthClient) => ({
|
v2: (projectId: string, client: AbstractModrinthClient) => ({
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { useAppQueryClient } from '@/composables/query-client'
|
||||||
|
|
||||||
export const useUser = async (force = false) => {
|
export const useUser = async (force = false) => {
|
||||||
const user = useState('user', () => {})
|
const user = useState('user', () => {})
|
||||||
|
|
||||||
@@ -158,5 +160,6 @@ export const logout = async () => {
|
|||||||
|
|
||||||
await useAuth('none')
|
await useAuth('none')
|
||||||
useCookie('auth-token').value = null
|
useCookie('auth-token').value = null
|
||||||
|
useAppQueryClient().clear()
|
||||||
stopLoading()
|
stopLoading()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useGeneratedState } from '~/composables/generated'
|
import { useGeneratedState } from '~/composables/generated'
|
||||||
|
import { projectQueryOptions } from '~/composables/queries/project'
|
||||||
import { useAppQueryClient } from '~/composables/query-client'
|
import { useAppQueryClient } from '~/composables/query-client'
|
||||||
import { getProjectTypeForUrlShorthand } from '~/helpers/projects.js'
|
import { getProjectTypeForUrlShorthand } from '~/helpers/projects.js'
|
||||||
import { useServerModrinthClient } from '~/server/utils/api-client'
|
import { useServerModrinthClient } from '~/server/utils/api-client'
|
||||||
@@ -21,11 +22,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|||||||
try {
|
try {
|
||||||
// Fetch v2 project for redirect check AND cache it for the page
|
// Fetch v2 project for redirect check AND cache it for the page
|
||||||
// Using fetchQuery ensures the page's useQuery gets this cached result
|
// Using fetchQuery ensures the page's useQuery gets this cached result
|
||||||
const project = await queryClient.fetchQuery({
|
const project = await queryClient.fetchQuery(projectQueryOptions.v2(projectId, client))
|
||||||
queryKey: ['project', 'v2', projectId],
|
|
||||||
queryFn: () => client.labrinth.projects_v2.get(projectId),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Let page handle 404
|
// Let page handle 404
|
||||||
if (!project) return
|
if (!project) return
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
:is-settings="route.name.startsWith('type-id-settings')"
|
:is-settings="route.name.startsWith('type-id-settings')"
|
||||||
:set-processing="setProcessing"
|
:set-processing="setProcessing"
|
||||||
:all-members="allMembers"
|
:all-members="allMembers"
|
||||||
:update-members="refreshMembers"
|
:update-members="invalidateProject"
|
||||||
:auth="auth"
|
:auth="auth"
|
||||||
:tags="tags"
|
:tags="tags"
|
||||||
/>
|
/>
|
||||||
@@ -747,7 +747,7 @@
|
|||||||
:collapsed="collapsedChecklist"
|
:collapsed="collapsedChecklist"
|
||||||
:toggle-collapsed="() => (collapsedChecklist = !collapsedChecklist)"
|
:toggle-collapsed="() => (collapsedChecklist = !collapsedChecklist)"
|
||||||
:all-members="allMembers"
|
:all-members="allMembers"
|
||||||
:update-members="updateMembers"
|
:update-members="invalidateProject"
|
||||||
:auth="auth"
|
:auth="auth"
|
||||||
:tags="tags"
|
:tags="tags"
|
||||||
/>
|
/>
|
||||||
@@ -1008,6 +1008,7 @@ import ModerationChecklist from '~/components/ui/moderation/checklist/Moderation
|
|||||||
import NavTabs from '~/components/ui/NavTabs.vue'
|
import NavTabs from '~/components/ui/NavTabs.vue'
|
||||||
import ProjectMemberHeader from '~/components/ui/ProjectMemberHeader.vue'
|
import ProjectMemberHeader from '~/components/ui/ProjectMemberHeader.vue'
|
||||||
import { saveFeatureFlags } from '~/composables/featureFlags.ts'
|
import { saveFeatureFlags } from '~/composables/featureFlags.ts'
|
||||||
|
import { STALE_TIME, STALE_TIME_LONG } from '~/composables/queries/project'
|
||||||
import { userCollectProject, userFollowProject } from '~/composables/user.js'
|
import { userCollectProject, userFollowProject } from '~/composables/user.js'
|
||||||
import { useModerationStore } from '~/store/moderation.ts'
|
import { useModerationStore } from '~/store/moderation.ts'
|
||||||
import { reportProject } from '~/utils/report-helpers.ts'
|
import { reportProject } from '~/utils/report-helpers.ts'
|
||||||
@@ -1472,14 +1473,10 @@ const client = injectModrinthClient()
|
|||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
// V2 Project - hits middleware cache (uses route param for lookup)
|
// V2 Project - hits middleware cache (uses route param for lookup)
|
||||||
const {
|
const { data: projectRaw, error: projectV2Error } = useQuery({
|
||||||
data: projectRaw,
|
|
||||||
error: projectV2Error,
|
|
||||||
refetch: resetProjectV2,
|
|
||||||
} = useQuery({
|
|
||||||
queryKey: computed(() => ['project', 'v2', routeProjectId.value]),
|
queryKey: computed(() => ['project', 'v2', routeProjectId.value]),
|
||||||
queryFn: () => client.labrinth.projects_v2.get(routeProjectId.value),
|
queryFn: () => client.labrinth.projects_v2.get(routeProjectId.value),
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: STALE_TIME,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Handle project not found - use showError since watch runs outside Nuxt context
|
// Handle project not found - use showError since watch runs outside Nuxt context
|
||||||
@@ -1522,26 +1519,18 @@ const project = computed(() => {
|
|||||||
const projectId = computed(() => projectRaw.value?.id)
|
const projectId = computed(() => projectRaw.value?.id)
|
||||||
|
|
||||||
// V3 Project
|
// V3 Project
|
||||||
const {
|
const { data: projectV3, error: _projectV3Error } = useQuery({
|
||||||
data: projectV3,
|
|
||||||
error: _projectV3Error,
|
|
||||||
refetch: resetProjectV3,
|
|
||||||
} = useQuery({
|
|
||||||
queryKey: computed(() => ['project', 'v3', projectId.value]),
|
queryKey: computed(() => ['project', 'v3', projectId.value]),
|
||||||
queryFn: () => client.labrinth.projects_v3.get(projectId.value),
|
queryFn: () => client.labrinth.projects_v3.get(projectId.value),
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: STALE_TIME,
|
||||||
enabled: computed(() => !!projectId.value),
|
enabled: computed(() => !!projectId.value),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Members
|
// Members
|
||||||
const {
|
const { data: allMembersRaw, error: _membersError } = useQuery({
|
||||||
data: allMembersRaw,
|
|
||||||
error: _membersError,
|
|
||||||
refetch: _resetMembers,
|
|
||||||
} = useQuery({
|
|
||||||
queryKey: computed(() => ['project', projectId.value, 'members']),
|
queryKey: computed(() => ['project', projectId.value, 'members']),
|
||||||
queryFn: () => client.labrinth.projects_v3.getMembers(projectId.value),
|
queryFn: () => client.labrinth.projects_v3.getMembers(projectId.value),
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: STALE_TIME,
|
||||||
enabled: computed(() => !!projectId.value),
|
enabled: computed(() => !!projectId.value),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1556,25 +1545,26 @@ const allMembers = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Dependencies - lazy loaded client-side only
|
// Dependencies - lazy loaded client-side only
|
||||||
|
const dependenciesEnabled = ref(false)
|
||||||
const {
|
const {
|
||||||
data: dependenciesRaw,
|
data: dependenciesRaw,
|
||||||
error: _dependenciesError,
|
error: _dependenciesError,
|
||||||
isFetching: dependenciesLoading,
|
isFetching: dependenciesLoading,
|
||||||
refetch: refetchDependencies,
|
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: computed(() => ['project', projectId.value, 'dependencies']),
|
queryKey: computed(() => ['project', projectId.value, 'dependencies']),
|
||||||
queryFn: () => client.labrinth.projects_v2.getDependencies(projectId.value),
|
queryFn: () => client.labrinth.projects_v2.getDependencies(projectId.value),
|
||||||
staleTime: 1000 * 60 * 10,
|
staleTime: STALE_TIME_LONG,
|
||||||
|
enabled: computed(() => !!projectId.value && dependenciesEnabled.value),
|
||||||
})
|
})
|
||||||
|
|
||||||
const dependencies = computed(() => dependenciesRaw.value ?? null)
|
const dependencies = computed(() => dependenciesRaw.value ?? null)
|
||||||
|
|
||||||
// V3 Versions - lazy loaded client-side only
|
// V3 Versions - lazy loaded client-side only
|
||||||
|
const versionsEnabled = ref(false)
|
||||||
const {
|
const {
|
||||||
data: versionsV3,
|
data: versionsV3,
|
||||||
error: _versionsV3Error,
|
error: _versionsV3Error,
|
||||||
isFetching: versionsV3Loading,
|
isFetching: versionsV3Loading,
|
||||||
refetch: resetVersionsV3,
|
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: computed(() => ['project', projectId.value, 'versions', 'v3']),
|
queryKey: computed(() => ['project', projectId.value, 'versions', 'v3']),
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
@@ -1582,15 +1572,16 @@ const {
|
|||||||
include_changelog: false,
|
include_changelog: false,
|
||||||
apiVersion: 3,
|
apiVersion: 3,
|
||||||
}),
|
}),
|
||||||
staleTime: 1000 * 60 * 10,
|
staleTime: STALE_TIME_LONG,
|
||||||
|
enabled: computed(() => !!projectId.value && versionsEnabled.value),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Organization
|
// Organization
|
||||||
// Only fetch organization if project belongs to one
|
// Only fetch organization if project belongs to one
|
||||||
const { data: organization, refetch: _resetOrganization } = useQuery({
|
const { data: organization } = useQuery({
|
||||||
queryKey: computed(() => ['project', projectId.value, 'organization']),
|
queryKey: computed(() => ['project', projectId.value, 'organization']),
|
||||||
queryFn: () => client.labrinth.projects_v3.getOrganization(projectId.value),
|
queryFn: () => client.labrinth.projects_v3.getOrganization(projectId.value),
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: STALE_TIME,
|
||||||
enabled: computed(() => !!projectId.value && !!projectRaw.value?.organization),
|
enabled: computed(() => !!projectId.value && !!projectRaw.value?.organization),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1616,17 +1607,13 @@ const versions = computed(() => {
|
|||||||
const versionsLoading = computed(() => versionsV3Loading.value)
|
const versionsLoading = computed(() => versionsV3Loading.value)
|
||||||
|
|
||||||
// Load versions on demand (client-side only)
|
// Load versions on demand (client-side only)
|
||||||
async function loadVersions() {
|
function loadVersions() {
|
||||||
// Skip if already loaded or loading
|
versionsEnabled.value = true
|
||||||
if (versionsV3.value || versionsV3Loading.value) return
|
|
||||||
await resetVersionsV3()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load dependencies on demand (client-side only)
|
// Load dependencies on demand (client-side only)
|
||||||
async function loadDependencies() {
|
function loadDependencies() {
|
||||||
// Skip if already loaded or loading
|
dependenciesEnabled.value = true
|
||||||
if (dependenciesRaw.value || dependenciesLoading.value) return
|
|
||||||
await refetchDependencies()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if project has versions using the ID array from the V2 project
|
// Check if project has versions using the ID array from the V2 project
|
||||||
@@ -1654,25 +1641,13 @@ async function updateProjectRoute() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resetProject() {
|
async function invalidateProject() {
|
||||||
await invalidateProjectQueries(projectId.value)
|
|
||||||
await resetProjectV2()
|
|
||||||
await resetProjectV3()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function resetVersions() {
|
|
||||||
await invalidateProjectQueries(projectId.value)
|
|
||||||
await resetVersionsV3()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to invalidate project queries after mutations settle
|
|
||||||
async function invalidateProjectQueries(projectId) {
|
|
||||||
await queryClient.invalidateQueries({ queryKey: ['project', 'v2', routeProjectId.value] })
|
await queryClient.invalidateQueries({ queryKey: ['project', 'v2', routeProjectId.value] })
|
||||||
if (routeProjectId.value !== projectId) {
|
if (routeProjectId.value !== projectId.value) {
|
||||||
await queryClient.invalidateQueries({ queryKey: ['project', 'v2', projectId] })
|
await queryClient.invalidateQueries({ queryKey: ['project', 'v2', projectId.value] })
|
||||||
}
|
}
|
||||||
await queryClient.invalidateQueries({ queryKey: ['project', 'v3', projectId] })
|
// Prefix match — invalidates members, versions, dependencies, organization
|
||||||
await queryClient.invalidateQueries({ queryKey: ['project', projectId, 'versions', 'v3'] })
|
await queryClient.invalidateQueries({ queryKey: ['project', projectId.value] })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mutation for patching project data
|
// Mutation for patching project data
|
||||||
@@ -1718,8 +1693,8 @@ const patchProjectMutation = useMutation({
|
|||||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||||
},
|
},
|
||||||
|
|
||||||
onSettled: async (_data, _error, { projectId }) => {
|
onSettled: async () => {
|
||||||
await invalidateProjectQueries(projectId)
|
await invalidateProject()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1763,8 +1738,8 @@ const patchStatusMutation = useMutation({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
onSettled: async (_data, _error, { projectId }) => {
|
onSettled: async () => {
|
||||||
await invalidateProjectQueries(projectId)
|
await invalidateProject()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1797,8 +1772,8 @@ const patchIconMutation = useMutation({
|
|||||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||||
},
|
},
|
||||||
|
|
||||||
onSettled: async (_data, _error, { projectId }) => {
|
onSettled: async () => {
|
||||||
await invalidateProjectQueries(projectId)
|
await invalidateProject()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1860,8 +1835,8 @@ const createGalleryItemMutation = useMutation({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
onSettled: async (_data, _error, { projectId }) => {
|
onSettled: async () => {
|
||||||
await invalidateProjectQueries(projectId)
|
await invalidateProject()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1922,8 +1897,8 @@ const editGalleryItemMutation = useMutation({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
onSettled: async (_data, _error, { projectId }) => {
|
onSettled: async () => {
|
||||||
await invalidateProjectQueries(projectId)
|
await invalidateProject()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1961,8 +1936,8 @@ const deleteGalleryItemMutation = useMutation({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
onSettled: async (_data, _error, { projectId }) => {
|
onSettled: async () => {
|
||||||
await invalidateProjectQueries(projectId)
|
await invalidateProject()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -2202,15 +2177,6 @@ async function deleteGalleryItem(imageUrl) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshMembers() {
|
|
||||||
// Simply invalidate and refetch - the computed allMembers will auto-update
|
|
||||||
await queryClient.invalidateQueries({ queryKey: ['project', projectId.value, 'members'] })
|
|
||||||
}
|
|
||||||
|
|
||||||
async function refreshOrganization() {
|
|
||||||
await queryClient.invalidateQueries({ queryKey: ['project', projectId.value, 'organization'] })
|
|
||||||
}
|
|
||||||
|
|
||||||
async function copyId() {
|
async function copyId() {
|
||||||
await navigator.clipboard.writeText(project.value.id)
|
await navigator.clipboard.writeText(project.value.id)
|
||||||
}
|
}
|
||||||
@@ -2266,8 +2232,7 @@ async function deleteVersion(id) {
|
|||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
})
|
})
|
||||||
|
|
||||||
// Refetch versions to reflect deletion (versions is a computed ref)
|
await invalidateProject()
|
||||||
await resetVersions()
|
|
||||||
|
|
||||||
stopLoading()
|
stopLoading()
|
||||||
}
|
}
|
||||||
@@ -2320,11 +2285,8 @@ provideProjectPageContext({
|
|||||||
dependencies,
|
dependencies,
|
||||||
dependenciesLoading: computed(() => dependenciesLoading.value),
|
dependenciesLoading: computed(() => dependenciesLoading.value),
|
||||||
|
|
||||||
// Refresh functions (invalidate + refetch)
|
// Invalidate all project queries (auto-refetches active ones)
|
||||||
refreshProject: resetProject,
|
invalidate: invalidateProject,
|
||||||
refreshVersions: resetVersions,
|
|
||||||
refreshMembers,
|
|
||||||
refreshOrganization,
|
|
||||||
|
|
||||||
// Lazy loading
|
// Lazy loading
|
||||||
loadVersions,
|
loadVersions,
|
||||||
|
|||||||
@@ -340,7 +340,6 @@ import {
|
|||||||
ConfirmModal,
|
ConfirmModal,
|
||||||
DropArea,
|
DropArea,
|
||||||
FileInput,
|
FileInput,
|
||||||
injectNotificationManager,
|
|
||||||
injectProjectPageContext,
|
injectProjectPageContext,
|
||||||
NewModal as Modal,
|
NewModal as Modal,
|
||||||
} from '@modrinth/ui'
|
} from '@modrinth/ui'
|
||||||
@@ -352,8 +351,13 @@ import { isPermission } from '~/utils/permissions.ts'
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
// Single DI injection
|
// Single DI injection
|
||||||
const { addNotification } = injectNotificationManager()
|
const {
|
||||||
const { projectV2: project, currentMember, refreshProject } = injectProjectPageContext()
|
projectV2: project,
|
||||||
|
currentMember,
|
||||||
|
createGalleryItem: contextCreateGalleryItem,
|
||||||
|
editGalleryItem: contextEditGalleryItem,
|
||||||
|
deleteGalleryItem: contextDeleteGalleryItem,
|
||||||
|
} = injectProjectPageContext()
|
||||||
|
|
||||||
// Template refs
|
// Template refs
|
||||||
const modalEditItem = useTemplateRef('modal_edit_item')
|
const modalEditItem = useTemplateRef('modal_edit_item')
|
||||||
@@ -486,37 +490,16 @@ async function createGalleryItem() {
|
|||||||
shouldPreventActions.value = true
|
shouldPreventActions.value = true
|
||||||
startLoading()
|
startLoading()
|
||||||
|
|
||||||
try {
|
const success = await contextCreateGalleryItem(
|
||||||
let url = `project/${project.value.id}/gallery?ext=${
|
editFile.value!,
|
||||||
editFile.value
|
editTitle.value || undefined,
|
||||||
? editFile.value.type.split('/')[editFile.value.type.split('/').length - 1]
|
editDescription.value || undefined,
|
||||||
: null
|
editFeatured.value,
|
||||||
}&featured=${editFeatured.value}`
|
editOrder.value ? Number(editOrder.value) : undefined,
|
||||||
|
)
|
||||||
if (editTitle.value) {
|
|
||||||
url += `&title=${encodeURIComponent(editTitle.value)}`
|
|
||||||
}
|
|
||||||
if (editDescription.value) {
|
|
||||||
url += `&description=${encodeURIComponent(editDescription.value)}`
|
|
||||||
}
|
|
||||||
if (editOrder.value) {
|
|
||||||
url += `&ordering=${editOrder.value}`
|
|
||||||
}
|
|
||||||
|
|
||||||
await useBaseFetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: editFile.value,
|
|
||||||
})
|
|
||||||
await refreshProject()
|
|
||||||
|
|
||||||
|
if (success) {
|
||||||
modalEditItem.value?.hide()
|
modalEditItem.value?.hide()
|
||||||
} catch (err: unknown) {
|
|
||||||
const error = err as { data?: { description?: string } }
|
|
||||||
addNotification({
|
|
||||||
title: 'An error occurred',
|
|
||||||
text: error.data?.description ?? String(err),
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stopLoading()
|
stopLoading()
|
||||||
@@ -526,34 +509,18 @@ async function createGalleryItem() {
|
|||||||
async function editGalleryItem() {
|
async function editGalleryItem() {
|
||||||
shouldPreventActions.value = true
|
shouldPreventActions.value = true
|
||||||
startLoading()
|
startLoading()
|
||||||
try {
|
|
||||||
let url = `project/${project.value.id}/gallery?url=${encodeURIComponent(
|
|
||||||
project.value!.gallery![editIndex.value].url,
|
|
||||||
)}&featured=${editFeatured.value}`
|
|
||||||
|
|
||||||
if (editTitle.value) {
|
const imageUrl = project.value!.gallery![editIndex.value].url
|
||||||
url += `&title=${encodeURIComponent(editTitle.value)}`
|
const success = await contextEditGalleryItem(
|
||||||
}
|
imageUrl,
|
||||||
if (editDescription.value) {
|
editTitle.value || undefined,
|
||||||
url += `&description=${encodeURIComponent(editDescription.value)}`
|
editDescription.value || undefined,
|
||||||
}
|
editFeatured.value,
|
||||||
if (editOrder.value) {
|
editOrder.value ? Number(editOrder.value) : undefined,
|
||||||
url += `&ordering=${editOrder.value}`
|
)
|
||||||
}
|
|
||||||
|
|
||||||
await useBaseFetch(url, {
|
if (success) {
|
||||||
method: 'PATCH',
|
|
||||||
})
|
|
||||||
|
|
||||||
await refreshProject()
|
|
||||||
modalEditItem.value?.hide()
|
modalEditItem.value?.hide()
|
||||||
} catch (err: unknown) {
|
|
||||||
const error = err as { data?: { description?: string } }
|
|
||||||
addNotification({
|
|
||||||
title: 'An error occurred',
|
|
||||||
text: error.data?.description ?? String(err),
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stopLoading()
|
stopLoading()
|
||||||
@@ -563,25 +530,8 @@ async function editGalleryItem() {
|
|||||||
async function deleteGalleryImage() {
|
async function deleteGalleryImage() {
|
||||||
startLoading()
|
startLoading()
|
||||||
|
|
||||||
try {
|
const imageUrl = project.value!.gallery![deleteIndex.value].url!
|
||||||
await useBaseFetch(
|
await contextDeleteGalleryItem(imageUrl)
|
||||||
`project/${project.value.id}/gallery?url=${encodeURIComponent(
|
|
||||||
project.value!.gallery![deleteIndex.value].url!,
|
|
||||||
)}`,
|
|
||||||
{
|
|
||||||
method: 'DELETE',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
await refreshProject()
|
|
||||||
} catch (err: unknown) {
|
|
||||||
const error = err as { data?: { description?: string } }
|
|
||||||
addNotification({
|
|
||||||
title: 'An error occurred',
|
|
||||||
text: error.data?.description ?? String(err),
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
stopLoading()
|
stopLoading()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ import {
|
|||||||
} from '~/helpers/projects.js'
|
} from '~/helpers/projects.js'
|
||||||
|
|
||||||
const { addNotification } = injectNotificationManager()
|
const { addNotification } = injectNotificationManager()
|
||||||
const { projectV2: project, currentMember, refreshProject } = injectProjectPageContext()
|
const { projectV2: project, currentMember, invalidate } = injectProjectPageContext()
|
||||||
|
|
||||||
const auth = await useAuth()
|
const auth = await useAuth()
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ async function setStatus(status) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
project.value.status = status
|
project.value.status = status
|
||||||
await refreshProject()
|
await invalidate()
|
||||||
thread.value = await useBaseFetch(`thread/${thread.value.id}`)
|
thread.value = await useBaseFetch(`thread/${thread.value.id}`)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addNotification({
|
addNotification({
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ const {
|
|||||||
currentMember,
|
currentMember,
|
||||||
patchProject,
|
patchProject,
|
||||||
patchIcon,
|
patchIcon,
|
||||||
refreshProject,
|
invalidate,
|
||||||
} = injectProjectPageContext()
|
} = injectProjectPageContext()
|
||||||
|
|
||||||
const flags = useFeatureFlags()
|
const flags = useFeatureFlags()
|
||||||
@@ -407,7 +407,7 @@ const deleteIcon = async () => {
|
|||||||
await useBaseFetch(`project/${project.value.id}/icon`, {
|
await useBaseFetch(`project/${project.value.id}/icon`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
})
|
})
|
||||||
await refreshProject()
|
await invalidate()
|
||||||
addNotification({
|
addNotification({
|
||||||
title: 'Project icon removed',
|
title: 'Project icon removed',
|
||||||
text: "Your project's icon has been removed.",
|
text: "Your project's icon has been removed.",
|
||||||
|
|||||||
@@ -562,9 +562,7 @@ const {
|
|||||||
organization,
|
organization,
|
||||||
allMembers,
|
allMembers,
|
||||||
currentMember,
|
currentMember,
|
||||||
refreshProject,
|
invalidate,
|
||||||
refreshOrganization,
|
|
||||||
refreshMembers,
|
|
||||||
} = injectProjectPageContext()
|
} = injectProjectPageContext()
|
||||||
|
|
||||||
const cosmetics = useCosmetics()
|
const cosmetics = useCosmetics()
|
||||||
@@ -838,7 +836,7 @@ async function updateOrgMember(index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updateMembers = async () => {
|
const updateMembers = async () => {
|
||||||
await Promise.all([refreshProject(), refreshOrganization(), refreshMembers()])
|
await invalidate()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ const {
|
|||||||
projectV2: project,
|
projectV2: project,
|
||||||
currentMember,
|
currentMember,
|
||||||
versions,
|
versions,
|
||||||
refreshVersions,
|
invalidate,
|
||||||
loadVersions,
|
loadVersions,
|
||||||
} = injectProjectPageContext()
|
} = injectProjectPageContext()
|
||||||
|
|
||||||
@@ -387,7 +387,7 @@ async function deleteVersion() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshVersions()
|
await invalidate()
|
||||||
selectedVersion.value = null
|
selectedVersion.value = null
|
||||||
|
|
||||||
stopLoading()
|
stopLoading()
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="version" class="version-page">
|
<div v-if="version" class="version-page">
|
||||||
<CreateProjectVersionModal
|
<CreateProjectVersionModal v-if="currentMember" ref="createProjectVersionModal" />
|
||||||
v-if="currentMember"
|
|
||||||
ref="createProjectVersionModal"
|
|
||||||
@save="handleVersionSaved"
|
|
||||||
/>
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
v-if="currentMember"
|
v-if="currentMember"
|
||||||
ref="modal_confirm"
|
ref="modal_confirm"
|
||||||
@@ -470,8 +466,7 @@ const {
|
|||||||
loadVersions,
|
loadVersions,
|
||||||
dependencies: contextDependencies,
|
dependencies: contextDependencies,
|
||||||
loadDependencies,
|
loadDependencies,
|
||||||
refreshVersions,
|
invalidate,
|
||||||
refreshProject,
|
|
||||||
} = injectProjectPageContext()
|
} = injectProjectPageContext()
|
||||||
|
|
||||||
// Load versions and dependencies in parallel
|
// Load versions and dependencies in parallel
|
||||||
@@ -619,8 +614,8 @@ if (route.params.version === 'create') {
|
|||||||
)) as any
|
)) as any
|
||||||
if (versionV3) {
|
if (versionV3) {
|
||||||
version.value = versionV3
|
version.value = versionV3
|
||||||
// Refresh versions cache to include this version
|
// Refresh cache to include this version
|
||||||
await refreshVersions()
|
await invalidate()
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// API fetch failed - version truly doesn't exist, will 404 below
|
// API fetch failed - version truly doesn't exist, will 404 below
|
||||||
@@ -733,10 +728,6 @@ function handleOpenEditVersionModal(versionId: string, projectId: string, stageI
|
|||||||
createProjectVersionModal.value?.openEditVersionModal(versionId, projectId, stageId)
|
createProjectVersionModal.value?.openEditVersionModal(versionId, projectId, stageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleVersionSaved() {
|
|
||||||
router.go(0) // reload page for new data
|
|
||||||
}
|
|
||||||
|
|
||||||
async function _onImageUpload(file: File) {
|
async function _onImageUpload(file: File) {
|
||||||
const response = await useImageUpload(file, { context: 'version' })
|
const response = await useImageUpload(file, { context: 'version' })
|
||||||
|
|
||||||
@@ -1070,7 +1061,7 @@ async function createDataPackVersionHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function resetProjectVersions() {
|
async function resetProjectVersions() {
|
||||||
await Promise.all([refreshVersions(), refreshProject()])
|
await invalidate()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ const { addNotification } = injectNotificationManager()
|
|||||||
const {
|
const {
|
||||||
projectV2: project,
|
projectV2: project,
|
||||||
currentMember,
|
currentMember,
|
||||||
refreshVersions,
|
invalidate,
|
||||||
versions,
|
versions,
|
||||||
versionsLoading,
|
versionsLoading,
|
||||||
loadVersions,
|
loadVersions,
|
||||||
@@ -379,7 +379,7 @@ async function deleteVersion() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshVersions()
|
await invalidate()
|
||||||
selectedVersion.value = null
|
selectedVersion.value = null
|
||||||
|
|
||||||
stopLoading()
|
stopLoading()
|
||||||
|
|||||||
@@ -149,10 +149,12 @@ import {
|
|||||||
IntlFormatted,
|
IntlFormatted,
|
||||||
useVIntl,
|
useVIntl,
|
||||||
} from '@modrinth/ui'
|
} from '@modrinth/ui'
|
||||||
|
import { useQueryClient } from '@tanstack/vue-query'
|
||||||
|
|
||||||
import HCaptcha from '@/components/ui/HCaptcha.vue'
|
import HCaptcha from '@/components/ui/HCaptcha.vue'
|
||||||
import { getAuthUrl, getLauncherRedirectUrl } from '@/composables/auth.js'
|
import { getAuthUrl, getLauncherRedirectUrl } from '@/composables/auth.js'
|
||||||
|
|
||||||
|
const queryClient = useQueryClient()
|
||||||
const { addNotification } = injectNotificationManager()
|
const { addNotification } = injectNotificationManager()
|
||||||
const { formatMessage } = useVIntl()
|
const { formatMessage } = useVIntl()
|
||||||
|
|
||||||
@@ -306,6 +308,7 @@ async function finishSignIn(token) {
|
|||||||
if (token) {
|
if (token) {
|
||||||
await useAuth(token)
|
await useAuth(token)
|
||||||
await useUser()
|
await useUser()
|
||||||
|
queryClient.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (route.query.redirect) {
|
if (route.query.redirect) {
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ import {
|
|||||||
normalizeChildren,
|
normalizeChildren,
|
||||||
useVIntl,
|
useVIntl,
|
||||||
} from '@modrinth/ui'
|
} from '@modrinth/ui'
|
||||||
|
import { useQueryClient } from '@tanstack/vue-query'
|
||||||
|
|
||||||
|
const queryClient = useQueryClient()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const { formatMessage } = useVIntl()
|
const { formatMessage } = useVIntl()
|
||||||
@@ -96,6 +98,7 @@ const subscribe = ref(true)
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await useAuth(route.query.authToken)
|
await useAuth(route.query.authToken)
|
||||||
await useUser()
|
await useUser()
|
||||||
|
queryClient.clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
async function continueSignUp() {
|
async function continueSignUp() {
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ export function createManageVersionContext(
|
|||||||
): ManageVersionContextValue {
|
): ManageVersionContextValue {
|
||||||
const { labrinth } = injectModrinthClient()
|
const { labrinth } = injectModrinthClient()
|
||||||
const { addNotification } = injectNotificationManager()
|
const { addNotification } = injectNotificationManager()
|
||||||
const { refreshVersions, projectV2 } = injectProjectPageContext()
|
const { invalidate, projectV2 } = injectProjectPageContext()
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const draftVersion = ref<Labrinth.Versions.v3.DraftVersion>(structuredClone(EMPTY_DRAFT_VERSION))
|
const draftVersion = ref<Labrinth.Versions.v3.DraftVersion>(structuredClone(EMPTY_DRAFT_VERSION))
|
||||||
@@ -660,7 +660,7 @@ export function createManageVersionContext(
|
|||||||
text: 'The version has been successfully added to your project.',
|
text: 'The version has been successfully added to your project.',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
})
|
})
|
||||||
await refreshVersions()
|
await invalidate()
|
||||||
onSave?.()
|
onSave?.()
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
addNotification({
|
addNotification({
|
||||||
@@ -734,7 +734,7 @@ export function createManageVersionContext(
|
|||||||
text: 'The version has been successfully saved to your project.',
|
text: 'The version has been successfully saved to your project.',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
})
|
})
|
||||||
await refreshVersions()
|
await invalidate()
|
||||||
onSave?.()
|
onSave?.()
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
addNotification({
|
addNotification({
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export class NuxtModrinthClient extends XHRUploadClient {
|
|||||||
|
|
||||||
protected async executeRequest<T>(url: string, options: RequestOptions): Promise<T> {
|
protected async executeRequest<T>(url: string, options: RequestOptions): Promise<T> {
|
||||||
try {
|
try {
|
||||||
// @ts-expect-error - $fetch is provided by Nuxt runtime
|
// @ts-expect-error - $fetch is provided by Nuxt
|
||||||
const response = await $fetch<T>(url, {
|
const response = await $fetch<T>(url, {
|
||||||
method: options.method ?? 'GET',
|
method: options.method ?? 'GET',
|
||||||
headers: options.headers,
|
headers: options.headers,
|
||||||
@@ -148,6 +148,8 @@ export class NuxtModrinthClient extends XHRUploadClient {
|
|||||||
params: options.params,
|
params: options.params,
|
||||||
timeout: options.timeout,
|
timeout: options.timeout,
|
||||||
signal: options.signal,
|
signal: options.signal,
|
||||||
|
// @ts-expect-error - import.meta is provided by Nuxt
|
||||||
|
cache: import.meta.server ? undefined : 'no-store',
|
||||||
})
|
})
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -17,15 +17,12 @@ export interface ProjectPageContext {
|
|||||||
dependencies: Ref<Labrinth.Projects.v2.DependencyInfo | null>
|
dependencies: Ref<Labrinth.Projects.v2.DependencyInfo | null>
|
||||||
dependenciesLoading: Ref<boolean>
|
dependenciesLoading: Ref<boolean>
|
||||||
|
|
||||||
// Refresh functions (invalidate + refetch)
|
// Invalidate all project queries (auto-refetches active ones)
|
||||||
refreshProject: () => Promise<void>
|
invalidate: () => Promise<void>
|
||||||
refreshVersions: () => Promise<void>
|
|
||||||
refreshMembers: () => Promise<void>
|
|
||||||
refreshOrganization: () => Promise<void>
|
|
||||||
|
|
||||||
// Lazy loading
|
// Lazy loading
|
||||||
loadVersions: () => Promise<void>
|
loadVersions: () => void
|
||||||
loadDependencies: () => Promise<void>
|
loadDependencies: () => void
|
||||||
|
|
||||||
// Mutation functions
|
// Mutation functions
|
||||||
patchProject: (data: Record<string, unknown>, quiet?: boolean) => Promise<boolean>
|
patchProject: (data: Record<string, unknown>, quiet?: boolean) => Promise<boolean>
|
||||||
|
|||||||
Reference in New Issue
Block a user