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

View File

@@ -10,8 +10,11 @@ import { ISO3166Module } from './iso3166'
import { KyrosContentV1Module } from './kyros/content/v1'
import { KyrosFilesV0Module } from './kyros/files/v0'
import { LabrinthVersionsV2Module, LabrinthVersionsV3Module } from './labrinth'
import { LabrinthAuthInternalModule } from './labrinth/auth/internal'
import { LabrinthBillingInternalModule } from './labrinth/billing/internal'
import { LabrinthCollectionsModule } from './labrinth/collections'
import { LabrinthOrganizationsV3Module } from './labrinth/organizations/v3'
import { LabrinthPayoutV3Module } from './labrinth/payout/v3'
import { LabrinthProjectsV2Module } from './labrinth/projects/v2'
import { LabrinthProjectsV3Module } from './labrinth/projects/v3'
import { LabrinthServerPingInternalModule } from './labrinth/server-ping/internal'
@@ -45,8 +48,11 @@ export const MODULE_REGISTRY = {
launchermeta_manifest_v0: LauncherMetaManifestV0Module,
kyros_content_v1: KyrosContentV1Module,
kyros_files_v0: KyrosFilesV0Module,
labrinth_auth_internal: LabrinthAuthInternalModule,
labrinth_billing_internal: LabrinthBillingInternalModule,
labrinth_collections: LabrinthCollectionsModule,
labrinth_organizations_v3: LabrinthOrganizationsV3Module,
labrinth_payout_v3: LabrinthPayoutV3Module,
labrinth_projects_v2: LabrinthProjectsV2Module,
labrinth_projects_v3: LabrinthProjectsV3Module,
labrinth_server_ping_internal: LabrinthServerPingInternalModule,

View File

@@ -0,0 +1,32 @@
import { AbstractModule } from '../../../core/abstract-module'
import type { Labrinth } from '../types'
export class LabrinthAuthInternalModule extends AbstractModule {
public getModuleID(): string {
return 'labrinth_auth_internal'
}
/**
* Check if the user is subscribed to the newsletter
*
* @returns Promise resolving to the subscription status
*/
public async getNewsletterStatus(): Promise<Labrinth.Auth.Internal.SubscriptionStatus> {
return this.client.request<Labrinth.Auth.Internal.SubscriptionStatus>('/auth/email/subscribe', {
api: 'labrinth',
version: 'internal',
method: 'GET',
})
}
/**
* Subscribe to the newsletter
*/
public async subscribeNewsletter(): Promise<void> {
return this.client.request('/auth/email/subscribe', {
api: 'labrinth',
version: 'internal',
method: 'POST',
})
}
}

View File

@@ -1,5 +1,8 @@
export * from './auth/internal'
export * from './billing/internal'
export * from './collections'
export * from './organizations/v3'
export * from './payout/v3'
export * from './projects/v2'
export * from './projects/v3'
export * from './server-ping/internal'

View File

@@ -0,0 +1,52 @@
import { AbstractModule } from '../../../core/abstract-module'
import type { Labrinth } from '../types'
export class LabrinthOrganizationsV3Module extends AbstractModule {
public getModuleID(): string {
return 'labrinth_organizations_v3'
}
/**
* Get an organization by ID or slug
*
* @param idOrSlug - Organization ID or slug
* @returns Promise resolving to the organization data
*
* @example
* ```typescript
* const org = await client.labrinth.organizations_v3.get('my-org')
* ```
*/
public async get(idOrSlug: string): Promise<Labrinth.Organizations.v3.Organization> {
return this.client.request<Labrinth.Organizations.v3.Organization>(
`/organization/${idOrSlug}`,
{
api: 'labrinth',
version: 3,
method: 'GET',
},
)
}
/**
* Get an organization's projects
*
* @param idOrSlug - Organization ID or slug
* @returns Promise resolving to the organization's projects
*
* @example
* ```typescript
* const projects = await client.labrinth.organizations_v3.getProjects('my-org')
* ```
*/
public async getProjects(idOrSlug: string): Promise<Labrinth.Projects.v3.Project[]> {
return this.client.request<Labrinth.Projects.v3.Project[]>(
`/organization/${idOrSlug}/projects`,
{
api: 'labrinth',
version: 3,
method: 'GET',
},
)
}
}

View File

@@ -0,0 +1,21 @@
import { AbstractModule } from '../../../core/abstract-module'
import type { Labrinth } from '../types'
export class LabrinthPayoutV3Module extends AbstractModule {
public getModuleID(): string {
return 'labrinth_payout_v3'
}
/**
* Get the authenticated user's payout balance
*
* @returns Promise resolving to the user's payout balance
*/
public async getBalance(): Promise<Labrinth.Payout.v3.PayoutBalance> {
return this.client.request<Labrinth.Payout.v3.PayoutBalance>('/payout/balance', {
api: 'labrinth',
version: 3,
method: 'GET',
})
}
}

View File

@@ -240,4 +240,19 @@ export class LabrinthProjectsV2Module extends AbstractModule {
params: { url },
})
}
/**
* Get random projects
*
* @param count - Number of random projects to return
* @returns Promise resolving to an array of random projects
*/
public async getRandom(count: number): Promise<Labrinth.Projects.v2.Project[]> {
return this.client.request<Labrinth.Projects.v2.Project[]>('/projects_random', {
api: 'labrinth',
version: 2,
method: 'GET',
params: { count: String(count) },
})
}
}

View File

@@ -147,6 +147,28 @@ export namespace Labrinth {
}
}
export namespace Payout {
export namespace v3 {
export type PayoutBalance = {
available: number
withdrawn_lifetime: number
withdrawn_ytd: number
pending: number
dates: Record<string, number>
requested_form_type: string | null
form_completion_status: string | null
}
}
}
export namespace Auth {
export namespace Internal {
export type SubscriptionStatus = {
subscribed: boolean
}
}
}
export namespace Projects {
export namespace v2 {
export type Environment = 'required' | 'optional' | 'unsupported' | 'unknown'

View File

@@ -6,6 +6,47 @@ export class LabrinthUsersV2Module extends AbstractModule {
return 'labrinth_users_v2'
}
/**
* Get a user by ID or username
*
* @param idOrUsername - The user's ID or username
* @returns Promise resolving to the user data
*
* @example
* ```typescript
* const user = await client.labrinth.users_v2.get('my_user')
* ```
*/
public async get(idOrUsername: string): Promise<Labrinth.Users.v2.User> {
return this.client.request<Labrinth.Users.v2.User>(`/user/${idOrUsername}`, {
api: 'labrinth',
version: 2,
method: 'GET',
})
}
/**
* Get multiple users by their IDs
*
* @param ids - Array of user IDs
* @returns Promise resolving to an array of users
*
* @example
* ```typescript
* const users = await client.labrinth.users_v2.getMultiple(['id1', 'id2'])
* ```
*/
public async getMultiple(ids: string[]): Promise<Labrinth.Users.v2.User[]> {
return this.client.request<Labrinth.Users.v2.User[]>(
`/users?ids=${encodeURIComponent(JSON.stringify(ids))}`,
{
api: 'labrinth',
version: 2,
method: 'GET',
},
)
}
/**
* Get a user's projects
*
@@ -24,4 +65,73 @@ export class LabrinthUsersV2Module extends AbstractModule {
method: 'GET',
})
}
/**
* Get a user's organizations
*
* @param idOrUsername - The user's ID or username
* @returns Promise resolving to an array of the user's organizations
*
* @example
* ```typescript
* const orgs = await client.labrinth.users_v2.getOrganizations('my_user')
* ```
*/
public async getOrganizations(
idOrUsername: string,
): Promise<Labrinth.Organizations.v3.Organization[]> {
return this.client.request<Labrinth.Organizations.v3.Organization[]>(
`/user/${idOrUsername}/organizations`,
{
api: 'labrinth',
version: 3,
method: 'GET',
},
)
}
/**
* Get a user's collections
*
* @param idOrUsername - The user's ID or username
* @returns Promise resolving to an array of the user's collections
*
* @example
* ```typescript
* const collections = await client.labrinth.users_v2.getCollections('my_user')
* ```
*/
public async getCollections(idOrUsername: string): Promise<Labrinth.Collections.Collection[]> {
return this.client.request<Labrinth.Collections.Collection[]>(
`/user/${idOrUsername}/collections`,
{
api: 'labrinth',
version: 3,
method: 'GET',
},
)
}
/**
* Update a user
*
* @param idOrUsername - The user's ID or username
* @param data - Fields to update
*
* @example
* ```typescript
* await client.labrinth.users_v2.patch('my_user', { role: 'admin' })
* ```
*/
public async patch(
idOrUsername: string,
data: Partial<Pick<Labrinth.Users.v2.User, 'badges' | 'role'>>,
): Promise<void> {
return this.client.request(`/user/${idOrUsername}`, {
api: 'labrinth',
version: 2,
method: 'PATCH',
body: data,
})
}
}