refactor: align files tab with content tab design (#5621)

* fix: files.vue bugs before styling changes

* feat: move files tab to shared layout structure

* fix: qa

* fix: qa

* fix: bugs

* fix: lint

* fix: admonition cleanup with progress + actions

* fix: cleanup

* fix: modals

* fix: admon title

* fix: i18n standard

* fix: lint + i18n pass

* fix: remove transition

* fix: type errors

* feat: files tab in app

* fix: qa

* fix: backup item minmax

* fix: use ContentPageHeader for server panel

* fix: lint

* fix: lint

* fix: lint

* feat: page leave safety

* fix: lint

* fix: cargo fmt fix

* fix: blank in prod

* fix: content card table stuff

* Revert "fix: blank in prod"

This reverts commit 74758fe185cf85a4a20355857f889cb091b97ace.

* fix: import

* feat: browse worlds/servers flow

* fix: worlds tab parity with content tab

* fix: perf bug + shader filter pill copy

* feat: singleplayer filter

* fix: ordering

* fix: breadcrumbs

* fix: lint

* fix: qa

* feat: store server proj id when adding to a non-linked instance

* fix: lint

* fix: i18n + qa

* fix: conflict

* qa: already installed modal + placeholders not server-specific

* fix: qa

* fix: add + edit server modals

* fix: qa

* fix: security

* fix: devin flags

* fix: lint

* chore: change file to break build cache

* fix: admon

* fix: import path stuff

* feat: qa

* fix: fmt fmt idiot

---------

Signed-off-by: Calum H. <calum@modrinth.com>
This commit is contained in:
Calum H.
2026-03-26 18:55:15 +00:00
committed by GitHub
parent 706eb800cb
commit 381ea51cce
170 changed files with 8052 additions and 4571 deletions

View File

@@ -13,6 +13,7 @@
:modpack-icon-url="linkedModpackProject?.icon_url ?? undefined"
:enable-toggle="!props.isServerInstance"
:get-overflow-options="getOverflowOptions"
:switch-version="handleSwitchVersion"
@update:enabled="handleModpackContentToggle"
@bulk:enable="handleModpackContentBulkToggle"
@bulk:disable="handleModpackContentBulkToggle"
@@ -47,7 +48,7 @@
"
:project-name="
updatingModpack
? (linkedModpackProject?.title ?? formatMessage(messages.modpackFallback))
? (linkedModpackProject?.title ?? formatMessage(commonMessages.modpackLabel))
: (updatingProject?.project?.title ?? updatingProject?.file_name)
"
:loading="loadingVersions"
@@ -65,7 +66,9 @@
import type { Labrinth } from '@modrinth/api-client'
import { ClipboardCopyIcon, FolderOpenIcon } from '@modrinth/assets'
import {
commonMessages,
ConfirmModpackUpdateModal,
ContentCardLayout as ContentPageLayout,
type ContentItem,
type ContentModpackCardCategory,
type ContentModpackCardProject,
@@ -82,7 +85,6 @@ import {
useDebugLogger,
useVIntl,
} from '@modrinth/ui'
import { ContentCardLayout as ContentPageLayout } from '@modrinth/ui'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { open } from '@tauri-apps/plugin-dialog'
import { openUrl } from '@tauri-apps/plugin-opener'
@@ -125,10 +127,6 @@ const messages = defineMessages({
id: 'app.instance.mods.share-text',
defaultMessage: "Check out the projects I'm using in my modpack!",
},
modpackFallback: {
id: 'app.instance.mods.modpack-fallback',
defaultMessage: 'Modpack',
},
successfullyUploaded: {
id: 'app.instance.mods.successfully-uploaded',
defaultMessage: 'Successfully uploaded',
@@ -141,30 +139,10 @@ const messages = defineMessages({
id: 'app.instance.mods.projects-were-added',
defaultMessage: '{count} projects were added',
},
updating: {
id: 'app.instance.mods.updating',
defaultMessage: 'Updating...',
},
installing: {
id: 'app.instance.mods.installing',
defaultMessage: 'Installing...',
},
contentTypeProject: {
id: 'app.instance.mods.content-type-project',
defaultMessage: 'project',
},
unknownVersion: {
id: 'app.instance.mods.unknown-version',
defaultMessage: 'Unknown',
},
showFile: {
id: 'app.instance.mods.show-file',
defaultMessage: 'Show file',
},
copyLink: {
id: 'app.instance.mods.copy-link',
defaultMessage: 'Copy link',
},
})
let savedModalState: ModpackContentModalState | null = null
@@ -283,7 +261,7 @@ async function handleUploadFiles() {
}
}
async function _toggleDisableMod(mod: ContentItem) {
async function toggleDisableMod(mod: ContentItem) {
try {
mod.file_path = await toggle_disable_project(props.instance.path, mod.file_path!)
mod.enabled = !mod.enabled
@@ -301,7 +279,7 @@ async function _toggleDisableMod(mod: ContentItem) {
}
}
const toggleDisableMod = useDebounceFn(_toggleDisableMod, 20)
const toggleDisableDebounced = useDebounceFn(toggleDisableMod, 20)
async function removeMod(mod: ContentItem) {
await remove_project(props.instance.path, mod.file_path!).catch(handleError)
@@ -354,6 +332,12 @@ async function updateProject(mod: ContentItem) {
}
async function switchProjectVersion(mod: ContentItem, version: Labrinth.Versions.v2.Version) {
isBulkOperating.value = true
mod.installing = true
if (mod.version) {
mod.version.id = version.id
mod.version.version_number = version.version_number
}
try {
await remove_project(props.instance.path, mod.file_path!)
const newPath = await add_project_from_version(props.instance.path, version.id)
@@ -364,20 +348,12 @@ async function switchProjectVersion(mod: ContentItem, version: Labrinth.Versions
}
mod.file_path = newPath
if (mod.version) {
mod.version.id = version.id
mod.version.version_number = version.version_number
}
trackEvent('InstanceProjectSwitchVersion', {
loader: props.instance.loader,
game_version: props.instance.game_version,
id: mod.project?.id,
name: mod.project?.title ?? mod.file_name,
project_type: mod.project_type,
})
} catch (err) {
handleError(err as Error)
} finally {
mod.installing = false
isBulkOperating.value = false
await initProjects()
}
}
@@ -468,11 +444,11 @@ async function handleSwitchVersion(item: ContentItem) {
}
async function handleModpackContentToggle(item: ContentItem) {
await toggleDisableMod(item)
await toggleDisableDebounced(item)
}
async function handleModpackContentBulkToggle(items: ContentItem[]) {
await Promise.all(items.map((item) => _toggleDisableMod(item)))
await Promise.all(items.map((item) => toggleDisableMod(item)))
}
async function handleModpackContent() {
@@ -539,7 +515,7 @@ async function fetchAndSpliceVersion(
async function handleVersionSelect(version: Labrinth.Versions.v2.Version) {
if (version.changelog != null) return
loadingChangelog.value = true
await fetchAndSpliceVersion(version.id, 'must_revalidate', handleError)
await fetchAndSpliceVersion(version.id, 'must_revalidate', handleError as (err: unknown) => void)
loadingChangelog.value = false
}
@@ -660,14 +636,14 @@ function getOverflowOptions(item: ContentItem): OverflowMenuOption[] {
const options: OverflowMenuOption[] = []
options.push({
id: formatMessage(messages.showFile),
id: formatMessage(commonMessages.showFileButton),
icon: FolderOpenIcon,
action: () => highlightModInProfile(props.instance.path, item.file_path),
})
if (item.project?.slug) {
options.push({
id: formatMessage(messages.copyLink),
id: formatMessage(commonMessages.copyLinkButton),
icon: ClipboardCopyIcon,
action: async () => {
await navigator.clipboard.writeText(
@@ -718,18 +694,13 @@ async function initProjects(cacheBehaviour?: CacheBehaviour) {
if (allCategories && modpackInfo.project.categories) {
const seen = new Set<string>()
linkedModpackCategories.value = allCategories
.filter((cat: { name: string }) => {
if (modpackInfo.project.categories.includes(cat.name) && !seen.has(cat.name)) {
seen.add(cat.name)
return true
}
return false
})
.map((cat: { name: string }) => ({
...cat,
name: cat.name.charAt(0).toUpperCase() + cat.name.slice(1),
}))
linkedModpackCategories.value = allCategories.filter((cat: { name: string }) => {
if (modpackInfo.project.categories.includes(cat.name) && !seen.has(cat.name)) {
seen.add(cat.name)
return true
}
return false
})
} else {
linkedModpackCategories.value = []
}
@@ -799,8 +770,8 @@ provideContentManager({
hasUpdate: linkedModpackHasUpdate.value,
disabled: isModpackUpdating.value,
disabledText: isModpackUpdating.value
? formatMessage(messages.updating)
: formatMessage(messages.installing),
? formatMessage(commonMessages.updatingLabel)
: formatMessage(commonMessages.installingLabel),
}
: null,
),
@@ -808,13 +779,18 @@ provideContentManager({
isBusy: isInstanceBusy,
isBulkOperating,
contentTypeLabel: ref(formatMessage(messages.contentTypeProject)),
toggleEnabled: toggleDisableMod,
bulkEnableItems: (items) =>
Promise.all(items.map((item) => _toggleDisableMod(item))).then(() => {}),
bulkDisableItems: (items) =>
Promise.all(items.map((item) => _toggleDisableMod(item))).then(() => {}),
toggleEnabled: toggleDisableDebounced,
bulkEnableItems: (items: ContentItem[]) =>
Promise.all(items.filter((item) => !item.enabled).map((item) => toggleDisableMod(item))).then(
() => {},
),
bulkDisableItems: (items: ContentItem[]) =>
Promise.all(items.filter((item) => item.enabled).map((item) => toggleDisableMod(item))).then(
() => {},
),
deleteItem: removeMod,
bulkDeleteItems: (items) => Promise.all(items.map((item) => removeMod(item))).then(() => {}),
bulkDeleteItems: (items: ContentItem[]) =>
Promise.all(items.map((item) => removeMod(item))).then(() => {}),
refresh: () => initProjects('must_revalidate'),
browse: handleBrowseContent,
uploadFiles: handleUploadFiles,
@@ -830,7 +806,7 @@ provideContentManager({
showContentHint,
dismissContentHint,
shareItems: handleShareItems,
mapToTableItem: (item) => ({
mapToTableItem: (item: ContentItem) => ({
id: item.id,
project: item.project ?? {
id: item.file_name,
@@ -843,7 +819,7 @@ provideContentManager({
: undefined,
version: item.version ?? {
id: item.file_name,
version_number: formatMessage(messages.unknownVersion),
version_number: formatMessage(commonMessages.unknownLabel),
file_name: item.file_name,
},
versionLink:
@@ -860,6 +836,7 @@ provideContentManager({
}
: undefined,
enabled: item.enabled,
installing: item.installing,
}),
filterPersistKey: props.instance.path,
})