From 87c86c7d0dad956ec74086e334d7c8ea78213793 Mon Sep 17 00:00:00 2001 From: "Calum H." Date: Tue, 17 Mar 2026 20:06:19 +0000 Subject: [PATCH 1/6] refactor: remove `useBaseFetch` for `@modrinth/api-client` (#5596) * Reapply "fix: start swapping useBaseFetch usages to api-client" This reverts commit f4f33db7019ea861addb2c66c204d736800b7b6c. * fix: bugs * fix: analytics * fix: lint --- CLAUDE.md | 3 +- .../src/components/ui/NotificationItem.vue | 4 +- .../src/components/ui/charts/ChartDisplay.vue | 9 +- .../components/ui/create/CreateLimitAlert.vue | 40 +- .../ui/dashboard/CreatorWithdrawModal.vue | 18 +- .../ui/dashboard/RevenueTransaction.vue | 27 +- .../src/components/ui/report/ReportView.vue | 17 +- .../src/components/ui/report/ReportsList.vue | 25 +- .../src/helpers/platform-notifications.ts | 93 ++--- apps/frontend/src/pages/[type]/[id].vue | 72 +--- .../src/pages/[type]/[id]/moderation.vue | 18 +- .../pages/[type]/[id]/settings/members.vue | 100 ++--- apps/frontend/src/pages/admin/affiliates.vue | 42 +- .../frontend/src/pages/admin/billing/[id].vue | 43 +- apps/frontend/src/pages/auth/authorize.vue | 44 +- .../src/pages/auth/reset-password.vue | 24 +- apps/frontend/src/pages/auth/sign-in.vue | 26 +- apps/frontend/src/pages/auth/sign-up.vue | 21 +- apps/frontend/src/pages/collection/[id].vue | 17 +- .../src/pages/dashboard/affiliate-links.vue | 43 +- .../src/pages/dashboard/analytics.vue | 4 +- .../src/pages/dashboard/collections.vue | 5 +- apps/frontend/src/pages/dashboard/index.vue | 11 +- .../src/pages/dashboard/notifications.vue | 12 +- .../src/pages/dashboard/organizations.vue | 8 +- .../src/pages/dashboard/revenue/index.vue | 49 +-- .../src/pages/dashboard/revenue/transfers.vue | 7 +- apps/frontend/src/pages/hosting/index.vue | 41 +- .../manage/[id]/options/properties.vue | 2 +- apps/frontend/src/pages/legal/cmp-info.vue | 10 +- .../src/pages/moderation/reports/[id].vue | 4 +- .../organization/[id]/settings/projects.vue | 52 ++- .../src/pages/settings/applications.vue | 42 +- .../src/pages/settings/authorizations.vue | 29 +- .../src/pages/settings/billing/charges.vue | 12 +- .../src/pages/settings/billing/index.vue | 61 +-- apps/frontend/src/pages/settings/pats.vue | 39 +- apps/frontend/src/pages/settings/sessions.vue | 15 +- .../src/providers/creator-withdraw.ts | 25 +- apps/frontend/src/utils/fetch-helpers.ts | 10 + .../src/modules/archon/servers/v0.ts | 19 +- .../src/modules/archon/servers/v1.ts | 1 + packages/api-client/src/modules/index.ts | 26 ++ .../modules/labrinth/affiliate/internal.ts | 72 ++++ .../src/modules/labrinth/auth/v2.ts | 87 ++++ .../src/modules/labrinth/globals/internal.ts | 22 + .../api-client/src/modules/labrinth/index.ts | 9 + .../src/modules/labrinth/limits/v3.ts | 41 ++ .../src/modules/labrinth/notifications/v2.ts | 128 ++++++ .../src/modules/labrinth/oauth/internal.ts | 208 ++++++++++ .../src/modules/labrinth/organizations/v3.ts | 70 ++++ .../src/modules/labrinth/pats/v2.ts | 66 +++ .../src/modules/labrinth/payout/v3.ts | 41 ++ .../src/modules/labrinth/payouts/v3.ts | 26 ++ .../src/modules/labrinth/projects/v2.ts | 27 ++ .../src/modules/labrinth/reports/v3.ts | 141 +++++++ .../src/modules/labrinth/sessions/v2.ts | 34 ++ .../src/modules/labrinth/tags/v2.ts | 29 ++ .../src/modules/labrinth/teams/v2.ts | 101 +++++ .../src/modules/labrinth/teams/v3.ts | 31 ++ .../src/modules/labrinth/threads/v3.ts | 22 + .../api-client/src/modules/labrinth/types.ts | 388 ++++++++++++++++++ .../src/modules/labrinth/users/v2.ts | 43 ++ .../affiliate/AffiliateLinkCard.vue | 8 +- 64 files changed, 2073 insertions(+), 691 deletions(-) create mode 100644 packages/api-client/src/modules/labrinth/affiliate/internal.ts create mode 100644 packages/api-client/src/modules/labrinth/auth/v2.ts create mode 100644 packages/api-client/src/modules/labrinth/globals/internal.ts create mode 100644 packages/api-client/src/modules/labrinth/limits/v3.ts create mode 100644 packages/api-client/src/modules/labrinth/notifications/v2.ts create mode 100644 packages/api-client/src/modules/labrinth/oauth/internal.ts create mode 100644 packages/api-client/src/modules/labrinth/pats/v2.ts create mode 100644 packages/api-client/src/modules/labrinth/payouts/v3.ts create mode 100644 packages/api-client/src/modules/labrinth/reports/v3.ts create mode 100644 packages/api-client/src/modules/labrinth/sessions/v2.ts create mode 100644 packages/api-client/src/modules/labrinth/tags/v2.ts create mode 100644 packages/api-client/src/modules/labrinth/teams/v2.ts create mode 100644 packages/api-client/src/modules/labrinth/teams/v3.ts diff --git a/CLAUDE.md b/CLAUDE.md index fe48257fea..4f94b3ce85 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,7 +30,7 @@ This is the Modrinth monorepo — it contains all Modrinth projects, both fronte | `api-client` | API client for Nuxt, Tauri, and Node/browser | | `app-lib` | Shared app library | | `blog` | Blog system and changelog data | -| `utils` | Shared utility functions | +| `utils` | Shared utility functions (mostly deprecated) | | `moderation` | Moderation utilities | | `daedalus` | Daedalus protocol | | `tooling-config` | ESLint, Prettier, TypeScript configs | @@ -85,6 +85,7 @@ Each project may have its own `CLAUDE.md` with detailed instructions: ### General - Do not create new non-source code files (e.g. Bash scripts, SQL scripts) unless explicitly prompted to - For Frontend, when doing lint checks, only use the `prepr` commands, do not use `typecheck` or `tsc` etc. +- Types in `@modrinth/utils` are considered highly outdated, if a component needs them, check if you can switch said component to use types from `packages/api-client` ## Edit Tool - Whitespace Handling (CLAUDE ONLY) diff --git a/apps/frontend/src/components/ui/NotificationItem.vue b/apps/frontend/src/components/ui/NotificationItem.vue index c56f32978a..bf6fac681d 100644 --- a/apps/frontend/src/components/ui/NotificationItem.vue +++ b/apps/frontend/src/components/ui/NotificationItem.vue @@ -328,6 +328,7 @@ import { Categories, CopyCode, DoubleIcon, + injectModrinthClient, injectNotificationManager, ProjectStatusBadge, useFormatDateTime, @@ -341,6 +342,7 @@ import { acceptTeamInvite, removeSelfFromTeam } from '~/helpers/teams' import ThreadSummary from './thread/ThreadSummary.vue' +const client = injectModrinthClient() const { addNotification } = injectNotificationManager() const emit = defineEmits(['update:notifications']) const formatRelativeTime = useRelativeTime() @@ -407,7 +409,7 @@ async function read() { ? props.notification.grouped_notifs.map((notif) => notif.id) : []), ] - const updateNotifs = await markAsRead(ids) + const updateNotifs = await markAsRead(client, ids) const newNotifs = updateNotifs(props.notifications) emit('update:notifications', newNotifs) } catch (err) { diff --git a/apps/frontend/src/components/ui/charts/ChartDisplay.vue b/apps/frontend/src/components/ui/charts/ChartDisplay.vue index 6966ea788e..39564d4cb1 100644 --- a/apps/frontend/src/components/ui/charts/ChartDisplay.vue +++ b/apps/frontend/src/components/ui/charts/ChartDisplay.vue @@ -357,7 +357,7 @@ const props = withDefaults( }, ) -const projects = ref(props.projects || []) +const projects = computed(() => props.projects || []) // const selectedChart = ref('downloads') const selectedChart = computed({ @@ -389,6 +389,13 @@ const tinyRevenueChart = ref() const selectedDisplayProjects = ref(props.projects || []) +watch( + () => props.projects, + (newProjects) => { + selectedDisplayProjects.value = newProjects || [] + }, +) + const removeProjectFromDisplay = (id: string) => { selectedDisplayProjects.value = selectedDisplayProjects.value.filter((p) => p.id !== id) } diff --git a/apps/frontend/src/components/ui/create/CreateLimitAlert.vue b/apps/frontend/src/components/ui/create/CreateLimitAlert.vue index 8dbc296769..1c628574fd 100644 --- a/apps/frontend/src/components/ui/create/CreateLimitAlert.vue +++ b/apps/frontend/src/components/ui/create/CreateLimitAlert.vue @@ -42,13 +42,18 @@ diff --git a/apps/frontend/src/pages/dashboard/collections.vue b/apps/frontend/src/pages/dashboard/collections.vue index 737c016db6..87ee588bcc 100644 --- a/apps/frontend/src/pages/dashboard/collections.vue +++ b/apps/frontend/src/pages/dashboard/collections.vue @@ -155,6 +155,7 @@ import { commonMessages, defineMessages, DropdownSelect, + injectModrinthClient, StyledInput, useCompactNumber, useVIntl, @@ -162,7 +163,6 @@ import { import { useQuery } from '@tanstack/vue-query' import CollectionCreateModal from '~/components/ui/create/CollectionCreateModal.vue' -import { useBaseFetch } from '~/composables/fetch.js' const { formatMessage } = useVIntl() const { formatCompactNumber, formatCompactNumberPlural } = useCompactNumber() @@ -216,6 +216,7 @@ useHead({ const auth = await useAuth() const user = await useUser() +const client = injectModrinthClient() if (import.meta.client) { await initUserFollows() @@ -225,7 +226,7 @@ const filterQuery = ref('') const { data: collections } = useQuery({ queryKey: ['user', auth.value.user.id, 'collections'], - queryFn: () => useBaseFetch(`user/${auth.value.user.id}/collections`, { apiVersion: 3 }), + queryFn: () => client.labrinth.users_v2.getCollections(auth.value.user.id), }) const route = useNativeRoute() diff --git a/apps/frontend/src/pages/dashboard/index.vue b/apps/frontend/src/pages/dashboard/index.vue index 68b3d2714c..5c1a818e6e 100644 --- a/apps/frontend/src/pages/dashboard/index.vue +++ b/apps/frontend/src/pages/dashboard/index.vue @@ -97,7 +97,7 @@ From 1cfbefff021c8e526c40a4401f3a4fda1597754d Mon Sep 17 00:00:00 2001 From: "Calum H." Date: Tue, 17 Mar 2026 21:49:46 +0000 Subject: [PATCH 4/6] fix: various fixes related to content tab on app and panel (#5605) * fix: content filtering client only * fix: browse content bug Fixes #5570 * fix: Applying Mods & Updates filters at the same time doesn't work Fixes #5602 * fix: Browsing content: going back resets filters and installed state Fixes #5598 * fix: Mod tile background flickers when toggling enabled/disabled state Fixes #5600 * fix: Overhaul of "Content" tab on instances broke a lot Fixes #5567 * fix: Latest App update replacing all mods icons with a datapack/rescourcepack Fixes #5556 * fix: billing page api-client ditch useBaseFetch * fix: remove org icon from project card items * fix: lint --- apps/app-frontend/src/pages/Browse.vue | 19 ++++------- apps/app-frontend/src/pages/instance/Mods.vue | 24 ++++++++++---- .../frontend/src/pages/admin/billing/[id].vue | 28 +++++++++------- .../components/ContentCardItem.vue | 9 ++--- .../composables/content-filtering.ts | 33 ++++++++++++------- .../src/layouts/shared/content-tab/layout.vue | 1 + .../content-tab/providers/content-manager.ts | 3 ++ .../wrapped/hosting/manage/content.vue | 5 +-- 8 files changed, 72 insertions(+), 50 deletions(-) diff --git a/apps/app-frontend/src/pages/Browse.vue b/apps/app-frontend/src/pages/Browse.vue index fddfe84fd9..ce2f8502e5 100644 --- a/apps/app-frontend/src/pages/Browse.vue +++ b/apps/app-frontend/src/pages/Browse.vue @@ -109,6 +109,10 @@ const instanceHideInstalled = ref(false) const newlyInstalled = ref([]) const isServerInstance = ref(false) +const allInstalledIds = computed( + () => new Set([...newlyInstalled.value, ...(installedProjectIds.value ?? [])]), +) + const PERSISTENT_QUERY_PARAMS = ['i', 'ai'] await initInstanceContext() @@ -485,17 +489,8 @@ async function refreshSearch() { link: `/browse/${projectType.value}`, query: params, }) - const queryString = Object.entries(params) - .flatMap(([key, value]) => { - const values = Array.isArray(value) ? value : [value] - return values - .filter((v): v is string => v != null) - .map((v) => `${encodeURIComponent(key)}=${encodeURIComponent(v)}`) - }) - .join('&') - const newUrl = `${route.path}${queryString ? '?' + queryString : ''}` - debugLog('updating URL', newUrl) - window.history.replaceState(window.history.state, '', newUrl) + debugLog('updating URL', params) + router.replace({ path: route.path, query: params }) loading.value = false debugLog('refreshSearch complete', { version }) @@ -947,7 +942,7 @@ previousFilterState.value = JSON.stringify({ loader.supported_project_types?.includes(projectType), ), ]" - :installed="result.installed || newlyInstalled.includes(result.project_id || '')" + :installed="result.installed || allInstalledIds.has(result.project_id || '')" @install=" (id) => { newlyInstalled.push(id) diff --git a/apps/app-frontend/src/pages/instance/Mods.vue b/apps/app-frontend/src/pages/instance/Mods.vue index a6e3316983..c3244a3ea5 100644 --- a/apps/app-frontend/src/pages/instance/Mods.vue +++ b/apps/app-frontend/src/pages/instance/Mods.vue @@ -781,11 +781,17 @@ provideContentManager({ linkedModpackProject.value ? { project: linkedModpackProject.value, - projectLink: `/project/${linkedModpackProject.value.slug ?? linkedModpackProject.value.id}`, + projectLink: { + path: `/project/${linkedModpackProject.value.slug ?? linkedModpackProject.value.id}`, + query: { i: props.instance.path }, + }, version: linkedModpackVersion.value ?? undefined, versionLink: linkedModpackProject.value && linkedModpackVersion.value - ? `/project/${linkedModpackProject.value.slug ?? linkedModpackProject.value.id}/version/${linkedModpackVersion.value.id}` + ? { + path: `/project/${linkedModpackProject.value.slug ?? linkedModpackProject.value.id}/version/${linkedModpackVersion.value.id}`, + query: { i: props.instance.path }, + } : undefined, owner: linkedModpackOwner.value ? { @@ -808,7 +814,7 @@ provideContentManager({ isPackLocked, isBusy: isInstanceBusy, isBulkOperating, - getItemId: (item) => item.file_name, + getItemId: (item) => item.file_path ?? item.file_name, contentTypeLabel: ref(formatMessage(messages.contentTypeProject)), toggleEnabled: toggleDisableMod, bulkEnableItems: (items) => @@ -832,14 +838,16 @@ provideContentManager({ dismissContentHint, shareItems: handleShareItems, mapToTableItem: (item) => ({ - id: item.file_name, + id: item.file_path ?? item.file_name, project: item.project ?? { id: item.file_name, slug: null, title: item.file_name.replace('.disabled', ''), icon_url: null, }, - projectLink: item.project?.id ? `/project/${item.project.id}` : undefined, + projectLink: item.project?.id + ? { path: `/project/${item.project.id}`, query: { i: props.instance.path } } + : undefined, version: item.version ?? { id: item.file_name, version_number: formatMessage(messages.unknownVersion), @@ -847,7 +855,10 @@ provideContentManager({ }, versionLink: item.project?.id && item.version?.id - ? `/project/${item.project.id}/version/${item.version.id}` + ? { + path: `/project/${item.project.id}/version/${item.version.id}`, + query: { i: props.instance.path }, + } : undefined, owner: item.owner ? { @@ -857,6 +868,7 @@ provideContentManager({ : undefined, enabled: item.enabled, }), + filterPersistKey: props.instance.path, }) await initProjects() diff --git a/apps/frontend/src/pages/admin/billing/[id].vue b/apps/frontend/src/pages/admin/billing/[id].vue index 2b8387d63a..98fafd5514 100644 --- a/apps/frontend/src/pages/admin/billing/[id].vue +++ b/apps/frontend/src/pages/admin/billing/[id].vue @@ -137,12 +137,12 @@ class="mb-4 flex items-center justify-between border-0 border-b border-solid border-divider pb-4" >
- -

{{ user.username }}'s subscriptions

+ +

{{ user?.username }}'s subscriptions

- +