refactor: removing useAsyncData for tanstack query (#5262)

* refactor: most places with useAsyncData replaced with tanstack query

* refactor report list and report view

* refactor organization page to use tanstack query

* fix types

* refactor collection page and include proper loading state

* fix followed projects proper loading state

* fix 404 handling

* fix organization loading and 404 states

* pnpm prepr

* refactor: remove useAsyncData on newsletter button

* refactor: remove useAsyncData on auth globals fetch

* refactor: settings/billing/index.vue to useQuery instead of useAsyncData

* refactor: user page to remove useAsyncData

* pnpm prepr

* fix reports pages

* fix notifications page

* fix billing page cannot read properties of null and prop warnings

* fix refresh causing 404 by removing useBaseFetch and use api-client

* fix stale data after removing organization from project

* pnpm prepr

* fix news erroring in build

* fix: project page loads header only after content

* fix: user page tanstack problems (start on migrating away from useBaseFetch)

* fix: start swapping useBaseFetch usages to api-client

* Revert "fix: start swapping useBaseFetch usages to api-client"

This reverts commit 3df3fab11d535159132b1288dd7cacc38282b553.

* fix: remove debug logging

* fix: lint

---------

Co-authored-by: Calum H. <calum@modrinth.com>
Co-authored-by: Calum H. (IMB11) <contact@cal.engineer>
This commit is contained in:
Truman Gao
2026-03-16 12:10:29 -07:00
committed by GitHub
parent d0c7575a23
commit 681ae5d1d8
53 changed files with 1686 additions and 1079 deletions
@@ -260,6 +260,7 @@ import {
useFormatDateTime,
useVIntl,
} from '@modrinth/ui'
import { useQuery } from '@tanstack/vue-query'
import Modal from '~/components/ui/Modal.vue'
import {
@@ -491,16 +492,14 @@ const loading = ref(false)
const auth = await useAuth()
const { data: usersApps, refresh } = await useAsyncData(
'usersApps',
() =>
const { data: usersApps, refetch: refresh } = useQuery({
queryKey: computed(() => ['user', auth.value?.user?.id, 'oauth_apps']),
queryFn: () =>
useBaseFetch(`user/${auth.value.user.id}/oauth_apps`, {
apiVersion: 3,
}),
{
watch: [auth],
},
)
enabled: computed(() => !!auth.value?.user?.id),
})
const setForm = (app) => {
if (app?.id) {
@@ -98,6 +98,7 @@ import {
injectNotificationManager,
useVIntl,
} from '@modrinth/ui'
import { useQuery } from '@tanstack/vue-query'
import { useScopes } from '~/composables/auth/scopes.ts'
@@ -116,42 +117,36 @@ useHead({
title: 'Authorizations - Modrinth',
})
const { data: usersApps, refresh } = await useAsyncData('userAuthorizations', () =>
useBaseFetch(`oauth/authorizations`, {
internal: true,
}),
)
const { data: usersApps, refetch: refresh } = useQuery({
queryKey: ['oauth', 'authorizations'],
queryFn: () =>
useBaseFetch(`oauth/authorizations`, {
internal: true,
}),
})
const { data: appInformation } = await useAsyncData(
'appInfo',
() => {
if (!usersApps.value?.length) return null
return useBaseFetch('oauth/apps', {
const { data: appInformation } = useQuery({
queryKey: computed(() => ['oauth', 'apps', usersApps.value?.map((c) => c.app_id)]),
queryFn: () =>
useBaseFetch('oauth/apps', {
internal: true,
query: {
ids: JSON.stringify(usersApps.value.map((c) => c.app_id)),
},
})
},
{
watch: usersApps,
},
)
}),
enabled: computed(() => !!usersApps.value?.length),
})
const { data: appCreatorsInformation } = await useAsyncData(
'appCreatorsInfo',
() => {
if (!appInformation.value?.length) return null
return useBaseFetch('users', {
const { data: appCreatorsInformation } = useQuery({
queryKey: computed(() => ['users', appInformation.value?.map((c) => c.created_by)]),
queryFn: () =>
useBaseFetch('users', {
query: {
ids: JSON.stringify(appInformation.value.map((c) => c.created_by)),
},
})
},
{
watch: appInformation,
},
)
}),
enabled: computed(() => !!appInformation.value?.length),
})
const appInfoLookup = computed(() => {
if (!usersApps.value || !appInformation.value || !appCreatorsInformation.value) {
@@ -39,6 +39,7 @@
</template>
<script setup>
import { Badge, Breadcrumbs, useFormatDateTime, useFormatPrice } from '@modrinth/ui'
import { useQuery } from '@tanstack/vue-query'
import { products } from '~/generated/state.json'
@@ -53,23 +54,22 @@ const formatDate = useFormatDateTime({
day: '2-digit',
})
const { data: charges } = await useAsyncData(
'billing/payments',
() => useBaseFetch('billing/payments', { internal: true }),
{
transform: (charges) => {
return charges
.filter((charge) => charge.status !== 'open' && charge.status !== 'cancelled')
.map((charge) => {
const product = products.find((product) =>
product.prices.some((price) => price.id === charge.price_id),
)
const { data: charges } = useQuery({
queryKey: ['billing', 'payments'],
queryFn: async () => {
const charges = await useBaseFetch('billing/payments', { internal: true })
return charges
.filter((charge) => charge.status !== 'open' && charge.status !== 'cancelled')
.map((charge) => {
const product = products.find((product) =>
product.prices.some((price) => price.id === charge.price_id),
)
charge.product = product
charge.product = product
return charge
})
},
return charge
})
},
)
placeholderData: [],
})
</script>
@@ -299,12 +299,14 @@
<div class="flex text-2xl font-bold text-contrast">
<span class="text-contrast">
{{
formatPrice(
getProductPrice(getPyroProduct(subscription), subscription.interval)
.prices.intervals[subscription.interval],
getProductPrice(getPyroProduct(subscription), subscription.interval)
.currency_code,
)
getProductPrice(getPyroProduct(subscription), subscription.interval)
? formatPrice(
getProductPrice(getPyroProduct(subscription), subscription.interval)
.prices.intervals[subscription.interval],
getProductPrice(getPyroProduct(subscription), subscription.interval)
.currency_code,
)
: ''
}}
</span>
<span>/{{ subscription.interval.replace('ly', '') }}</span>
@@ -450,6 +452,7 @@
@proceed="removePaymentMethod(removePaymentMethodIndex)"
/>
<PurchaseModal
v-if="customer && paymentMethods"
ref="midasPurchaseModal"
:product="midasProduct"
:country="country"
@@ -620,6 +623,7 @@ import {
useVIntl,
} from '@modrinth/ui'
import { calculateSavings, getCurrency } from '@modrinth/utils'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { computed, ref } from 'vue'
import { useBaseFetch } from '@/composables/fetch.js'
@@ -734,25 +738,32 @@ const messages = defineMessages({
},
})
const [
{ data: paymentMethods, refresh: refreshPaymentMethods },
{ data: charges, refresh: refreshCharges },
{ data: customer, refresh: refreshCustomer },
{ data: subscriptions, refresh: refreshSubscriptions },
{ data: productsData, refresh: refreshProducts },
{ data: serversData, refresh: refreshServers },
] = await Promise.all([
useAsyncData('billing/payment_methods', () =>
useBaseFetch('billing/payment_methods', { internal: true }),
),
useAsyncData('billing/payments', () => useBaseFetch('billing/payments', { internal: true })),
useAsyncData('billing/customer', () => useBaseFetch('billing/customer', { internal: true })),
useAsyncData('billing/subscriptions', () =>
useBaseFetch('billing/subscriptions', { internal: true }),
),
useAsyncData('billing/products', () => useBaseFetch('billing/products', { internal: true })),
useAsyncData('servers', () => useServersFetch('servers')),
])
const queryClient = useQueryClient()
const { data: paymentMethods } = useQuery({
queryKey: ['billing', 'payment_methods'],
queryFn: () => useBaseFetch('billing/payment_methods', { internal: true }),
})
const { data: charges } = useQuery({
queryKey: ['billing', 'payments'],
queryFn: () => useBaseFetch('billing/payments', { internal: true }),
})
const { data: customer } = useQuery({
queryKey: ['billing', 'customer'],
queryFn: () => useBaseFetch('billing/customer', { internal: true }),
})
const { data: subscriptions } = useQuery({
queryKey: ['billing', 'subscriptions'],
queryFn: () => useBaseFetch('billing/subscriptions', { internal: true }),
})
const { data: productsData } = useQuery({
queryKey: ['billing', 'products'],
queryFn: () => useBaseFetch('billing/products', { internal: true }),
})
const { data: serversData } = useQuery({
queryKey: ['servers'],
queryFn: () => useServersFetch('servers'),
})
const midasProduct = ref(products.find((x) => x.metadata?.type === 'midas'))
const midasSubscription = computed(() =>
@@ -996,12 +1007,8 @@ const resubscribePyro = async (subscriptionId, wasSuspended) => {
const refresh = async () => {
await Promise.all([
refreshPaymentMethods(),
refreshCharges(),
refreshCustomer(),
refreshSubscriptions(),
refreshProducts(),
refreshServers(),
queryClient.invalidateQueries({ queryKey: ['billing'] }),
queryClient.invalidateQueries({ queryKey: ['servers'] }),
])
}
+6 -1
View File
@@ -203,6 +203,7 @@ import {
useRelativeTime,
useVIntl,
} from '@modrinth/ui'
import { useQuery } from '@tanstack/vue-query'
import Modal from '~/components/ui/Modal.vue'
import {
@@ -326,7 +327,11 @@ const deletePatIndex = ref(null)
const loading = ref(false)
const { data: pats, refresh } = await useAsyncData('pat', () => useBaseFetch('pat'))
const { data: pats, refetch: refresh } = useQuery({
queryKey: ['pat'],
queryFn: () => useBaseFetch('pat'),
placeholderData: [],
})
const displayPats = computed(() => {
return pats.value.toSorted((a, b) => new Date(b.created) - new Date(a.created))
})
@@ -52,6 +52,7 @@ import {
useRelativeTime,
useVIntl,
} from '@modrinth/ui'
import { useQuery } from '@tanstack/vue-query'
definePageMeta({
middleware: 'auth',
@@ -101,9 +102,10 @@ useHead({
title: () => `${formatMessage(commonSettingsMessages.sessions)} - Modrinth`,
})
const { data: sessions, refresh } = await useAsyncData('session/list', () =>
useBaseFetch('session/list'),
)
const { data: sessions, refetch: refresh } = useQuery({
queryKey: ['session', 'list'],
queryFn: () => useBaseFetch('session/list'),
})
async function revokeSession(id) {
startLoading()