Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

refactor: removing useAsyncData for tanstack query #5262

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
tdgao wants to merge 14 commits into main
base: main
Choose a base branch
Loading
from truman/ditching-useAsyncData
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
fd9aa86
refactor: most places with useAsyncData replaced with tanstack query
tdgao Feb 1, 2026
98d95b6
refactor report list and report view
tdgao Feb 2, 2026
4cd2150
refactor organization page to use tanstack query
tdgao Feb 2, 2026
3324643
fix types
tdgao Feb 2, 2026
ce639de
refactor collection page and include proper loading state
tdgao Feb 2, 2026
40d9865
fix followed projects proper loading state
tdgao Feb 2, 2026
4930830
fix 404 handling
tdgao Feb 2, 2026
56167a3
fix organization loading and 404 states
tdgao Feb 2, 2026
d65db6a
pnpm prepr
tdgao Feb 2, 2026
36da8cc
Merge branch 'main' into truman/ditching-useAsyncData
tdgao Feb 14, 2026
eb2ddd6
refactor: remove useAsyncData on newsletter button
tdgao Feb 14, 2026
80d4676
refactor: remove useAsyncData on auth globals fetch
tdgao Feb 14, 2026
3dda4f2
refactor: settings/billing/index.vue to useQuery instead of useAsyncData
tdgao Feb 14, 2026
240d6eb
refactor: user page to remove useAsyncData
tdgao Feb 14, 2026
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
58 changes: 39 additions & 19 deletions apps/frontend/src/components/ui/NewsletterButton.vue
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
<script setup lang="ts">
import { CheckIcon, MailIcon } from '@modrinth/assets'
import { ButtonStyled } from '@modrinth/ui'
import { ref } from 'vue'
import { ButtonStyled, defineMessages, useVIntl } from '@modrinth/ui'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { computed, ref } from 'vue'

import { useBaseFetch } from '~/composables/fetch.js'

const auth = await useAuth()
const { formatMessage } = useVIntl()

const messages = defineMessages({
tooltipSubscribe: {
id: 'ui.newsletter-button.tooltip',
defaultMessage: 'Subscribe to the Modrinth newsletter',
},
subscribe: {
id: 'ui.newsletter-button.subscribe',
defaultMessage: 'Subscribe',
},
subscribed: {
id: 'ui.newsletter-button.subscribed',
defaultMessage: 'Subscribed!',
},
})

const auth = (await useAuth()) as unknown as {
value: { user: { id: string; username: string; email: string; created: string } }
}
const queryClient = useQueryClient()
const showSubscriptionConfirmation = ref(false)
const showSubscribeButton = useAsyncData(
async () => {

const { data: showSubscribeButton, isSuccess } = useQuery({
queryKey: computed(() => ['newsletter', 'subscribed', auth.value.user.id]),
queryFn: async () => {
if (auth.value?.user) {
try {
const { subscribed } = await useBaseFetch('auth/email/subscribe', {
const { subscribed } = (await useBaseFetch('auth/email/subscribe', {
method: 'GET',
})
})) as { subscribed: boolean }
return !subscribed
} catch {
return true
Expand All @@ -22,8 +45,8 @@ const showSubscribeButton = useAsyncData(
return false
}
},
{ watch: [auth], server: false },
)
enabled: computed(() => !!auth.value?.user),
})

async function subscribe() {
try {
Expand All @@ -36,22 +59,19 @@ async function subscribe() {
} finally {
setTimeout(() => {
showSubscriptionConfirmation.value = false
showSubscribeButton.status.value = 'success'
showSubscribeButton.data.value = false
queryClient.setQueryData(['newsletter', 'subscribed', auth.value?.user?.id], false)
}, 2500)
}
}
</script>

<template>
<ButtonStyled
v-if="showSubscribeButton.status.value === 'success' && showSubscribeButton.data.value"
color="brand"
type="outlined"
>
<button v-tooltip="`Subscribe to the Modrinth newsletter`" @click="subscribe">
<template v-if="!showSubscriptionConfirmation"> <MailIcon /> Subscribe </template>
<template v-else> <CheckIcon /> Subscribed! </template>
<ButtonStyled v-if="isSuccess && showSubscribeButton" color="brand" type="outlined">
<button v-tooltip="formatMessage(messages.tooltipSubscribe)" @click="subscribe">
<template v-if="!showSubscriptionConfirmation">
<MailIcon /> {{ formatMessage(messages.subscribe) }}
</template>
<template v-else> <CheckIcon /> {{ formatMessage(messages.subscribed) }} </template>
</button>
</ButtonStyled>
</template>
11 changes: 7 additions & 4 deletions apps/frontend/src/components/ui/create/CreateLimitAlert.vue
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@
import { MessageIcon } from '@modrinth/assets'
import { Admonition, ButtonStyled, defineMessages, useVIntl } from '@modrinth/ui'
import { capitalizeString } from '@modrinth/utils'
import { useQuery } from '@tanstack/vue-query'
import { computed, watch } from 'vue'

import { useBaseFetch } from '~/composables/fetch.js'

const { formatMessage } = useVIntl()

const messages = defineMessages({
Expand Down Expand Up @@ -121,10 +124,10 @@ const apiEndpoint = computed(() => {
}
})

const { data: limits } = await useAsyncData<UserLimits | undefined>(
`limits-${props.type}`,
() => useBaseFetch(apiEndpoint.value, { apiVersion: 3 }) as Promise<UserLimits>,
)
const { data: limits } = useQuery({
queryKey: computed(() => ['limits', props.type]),
queryFn: () => useBaseFetch(apiEndpoint.value, { apiVersion: 3 }) as Promise<UserLimits>,
})

const typeName = computed<{ singular: string; plural: string }>(() => {
switch (props.type) {
Expand Down
130 changes: 75 additions & 55 deletions apps/frontend/src/components/ui/report/ReportView.vue
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@
</div>
</template>
<script setup>
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { computed } from 'vue'

import Breadcrumbs from '~/components/ui/Breadcrumbs.vue'
import ReportInfo from '~/components/ui/report/ReportInfo.vue'
import ConversationThread from '~/components/ui/thread/ConversationThread.vue'
import { useBaseFetch } from '~/composables/fetch.js'
import { addReportMessage } from '~/helpers/threads.js'

const props = defineProps({
Expand All @@ -41,74 +45,90 @@ const props = defineProps({
},
})

const report = ref(null)
const queryClient = useQueryClient()

await fetchReport().then((result) => {
report.value = result
// Fetch raw report
const { data: rawReport } = useQuery({
queryKey: computed(() => ['report', props.reportId]),
queryFn: async () => {
const data = await useBaseFetch(`report/${props.reportId}`)
data.item_id = data.item_id.replace(/"/g, '')
return data
},
})

const { data: rawThread } = await useAsyncData(`thread/${report.value.thread_id}`, () =>
useBaseFetch(`thread/${report.value.thread_id}`),
)
const thread = computed(() => addReportMessage(rawThread.value, report.value))
// Compute user IDs needed
const userIds = computed(() => {
if (!rawReport.value) return []
const ids = [rawReport.value.reporter]
if (rawReport.value.item_type === 'user') {
ids.push(rawReport.value.item_id)
}
return ids
})

async function updateThread(newThread) {
rawThread.value = newThread
report.value = await fetchReport()
}
// Fetch users
const { data: users } = useQuery({
queryKey: computed(() => ['users', userIds.value]),
queryFn: () => useBaseFetch(`users?ids=${encodeURIComponent(JSON.stringify(userIds.value))}`),
enabled: computed(() => userIds.value.length > 0),
})

async function fetchReport() {
const { data: rawReport } = await useAsyncData(`report/${props.reportId}`, () =>
useBaseFetch(`report/${props.reportId}`),
)
rawReport.value.item_id = rawReport.value.item_id.replace(/"/g, '')
// Version ID if applicable
const versionId = computed(() =>
rawReport.value?.item_type === 'version' ? rawReport.value.item_id : null,
)

const userIds = []
userIds.push(rawReport.value.reporter)
if (rawReport.value.item_type === 'user') {
userIds.push(rawReport.value.item_id)
}
// Fetch version
const { data: version } = useQuery({
queryKey: computed(() => ['version', versionId.value]),
queryFn: () => useBaseFetch(`version/${versionId.value}`),
enabled: computed(() => !!versionId.value),
})

const versionId = rawReport.value.item_type === 'version' ? rawReport.value.item_id : null
// Project ID
const projectId = computed(() => {
if (version.value) return version.value.project_id
if (rawReport.value?.item_type === 'project') return rawReport.value.item_id
return null
})

let users = []
if (userIds.length > 0) {
const { data: usersVal } = await useAsyncData(`users?ids=${JSON.stringify(userIds)}`, () =>
useBaseFetch(`users?ids=${encodeURIComponent(JSON.stringify(userIds))}`),
)
users = usersVal.value
}
// Fetch project
const { data: project } = useQuery({
queryKey: computed(() => ['project', projectId.value]),
queryFn: () => useBaseFetch(`project/${projectId.value}`),
enabled: computed(() => !!projectId.value),
})

let version = null
if (versionId) {
const { data: versionVal } = await useAsyncData(`version/${versionId}`, () =>
useBaseFetch(`version/${versionId}`),
)
version = versionVal.value
// Assemble the full report object
const report = computed(() => {
if (!rawReport.value) return null
return {
...rawReport.value,
project: project.value ?? null,
version: version.value ?? null,
reporterUser: (users.value || []).find((user) => user.id === rawReport.value.reporter),
user:
rawReport.value.item_type === 'user'
? (users.value || []).find((user) => user.id === rawReport.value.item_id)
: undefined,
}
})

const projectId = version
? version.project_id
: rawReport.value.item_type === 'project'
? rawReport.value.item_id
: null
// Fetch thread
const { data: rawThread } = useQuery({
queryKey: computed(() => ['thread', report.value?.thread_id]),
queryFn: () => useBaseFetch(`thread/${report.value.thread_id}`),
enabled: computed(() => !!report.value?.thread_id),
})

let project = null
if (projectId) {
const { data: projectVal } = await useAsyncData(`project/${projectId}`, () =>
useBaseFetch(`project/${projectId}`),
)
project = projectVal.value
}
const thread = computed(() =>
rawThread.value && report.value ? addReportMessage(rawThread.value, report.value) : null,
)

const reportData = rawReport.value
reportData.project = project
reportData.version = version
reportData.reporterUser = users.find((user) => user.id === rawReport.value.reporter)
if (rawReport.value.item_type === 'user') {
reportData.user = users.find((user) => user.id === rawReport.value.item_id)
}
return reportData
async function updateThread(newThread) {
queryClient.setQueryData(['thread', report.value?.thread_id], newThread)
await queryClient.invalidateQueries({ queryKey: ['report', props.reportId] })
}
</script>
<style lang="scss" scoped>
Expand Down
Loading
Loading

AltStyle によって変換されたページ (->オリジナル) /