feat: better tooltips for mods in content tab hosting panel (#5679)
* feat: better tooltips for mods in content tab hosting panel * feat: qa
This commit is contained in:
@@ -203,13 +203,18 @@ const addonsQuery = useQuery({
|
|||||||
|
|
||||||
const modpack = computed(() => addonsQuery.data.value?.modpack ?? null)
|
const modpack = computed(() => addonsQuery.data.value?.modpack ?? null)
|
||||||
|
|
||||||
|
const modpackProjectId = computed(() => {
|
||||||
|
const spec = modpack.value?.spec
|
||||||
|
return spec?.platform === 'modrinth' ? spec.project_id : null
|
||||||
|
})
|
||||||
|
|
||||||
const modpackVersionsQuery = useQuery({
|
const modpackVersionsQuery = useQuery({
|
||||||
queryKey: computed(() => ['labrinth', 'versions', 'v2', modpack.value?.spec.project_id]),
|
queryKey: computed(() => ['labrinth', 'versions', 'v2', modpackProjectId.value]),
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
client.labrinth.versions_v2.getProjectVersions(modpack.value!.spec.project_id, {
|
client.labrinth.versions_v2.getProjectVersions(modpackProjectId.value!, {
|
||||||
include_changelog: false,
|
include_changelog: false,
|
||||||
}),
|
}),
|
||||||
enabled: computed(() => !!modpack.value?.spec.project_id),
|
enabled: computed(() => !!modpackProjectId.value),
|
||||||
})
|
})
|
||||||
|
|
||||||
const auth = await useAuth()
|
const auth = await useAuth()
|
||||||
@@ -321,17 +326,20 @@ provideInstallationSettings({
|
|||||||
}),
|
}),
|
||||||
isLinked: computed(() => {
|
isLinked: computed(() => {
|
||||||
const val = !!modpack.value
|
const val = !!modpack.value
|
||||||
debug('isLinked:', val, 'modpack:', modpack.value?.spec?.project_id)
|
debug('isLinked:', val, 'modpack:', modpackProjectId.value)
|
||||||
return val
|
return val
|
||||||
}),
|
}),
|
||||||
isBusy: isInstalling,
|
isBusy: isInstalling,
|
||||||
modpack: computed(() => {
|
modpack: computed(() => {
|
||||||
if (!modpack.value) return null
|
if (!modpack.value) return null
|
||||||
|
const isLocal = modpack.value.spec.platform === 'local_file'
|
||||||
return {
|
return {
|
||||||
iconUrl: modpack.value.icon_url,
|
iconUrl: modpack.value.icon_url,
|
||||||
title: modpack.value.title ?? modpack.value.spec.project_id,
|
title:
|
||||||
link: `/project/${modpack.value.spec.project_id}`,
|
modpack.value.title ?? (isLocal ? modpack.value.spec.name : modpack.value.spec.project_id),
|
||||||
|
link: modpackProjectId.value ? `/project/${modpackProjectId.value}` : undefined,
|
||||||
versionNumber: modpack.value.version_number,
|
versionNumber: modpack.value.version_number,
|
||||||
|
filename: isLocal ? modpack.value.spec.filename : undefined,
|
||||||
owner: modpack.value.owner
|
owner: modpack.value.owner
|
||||||
? {
|
? {
|
||||||
id: modpack.value.owner.id,
|
id: modpack.value.owner.id,
|
||||||
@@ -460,7 +468,7 @@ provideInstallationSettings({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async reinstallModpack() {
|
async reinstallModpack() {
|
||||||
if (!modpack.value) return
|
if (!modpack.value || modpack.value.spec.platform !== 'modrinth') return
|
||||||
debug(
|
debug(
|
||||||
'reinstallModpack: called, project:',
|
'reinstallModpack: called, project:',
|
||||||
modpack.value.spec.project_id,
|
modpack.value.spec.project_id,
|
||||||
@@ -531,10 +539,11 @@ provideInstallationSettings({
|
|||||||
getCachedModpackVersions: () => modpackVersionsQuery.data.value ?? null,
|
getCachedModpackVersions: () => modpackVersionsQuery.data.value ?? null,
|
||||||
|
|
||||||
async fetchModpackVersions() {
|
async fetchModpackVersions() {
|
||||||
debug('fetchModpackVersions: called, project:', modpack.value?.spec.project_id)
|
debug('fetchModpackVersions: called, project:', modpackProjectId.value)
|
||||||
|
if (!modpackProjectId.value) throw new Error('No modpack project ID')
|
||||||
try {
|
try {
|
||||||
const versions = await client.labrinth.versions_v2.getProjectVersions(
|
const versions = await client.labrinth.versions_v2.getProjectVersions(
|
||||||
modpack.value!.spec.project_id,
|
modpackProjectId.value,
|
||||||
{
|
{
|
||||||
include_changelog: false,
|
include_changelog: false,
|
||||||
},
|
},
|
||||||
@@ -562,7 +571,7 @@ provideInstallationSettings({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async onModpackVersionConfirm(version) {
|
async onModpackVersionConfirm(version) {
|
||||||
if (!modpack.value) return
|
if (!modpackProjectId.value) return
|
||||||
debug('onModpackVersionConfirm: called, version:', version.id)
|
debug('onModpackVersionConfirm: called, version:', version.id)
|
||||||
debug('onModpackVersionConfirm: emitting reinstall before API call')
|
debug('onModpackVersionConfirm: emitting reinstall before API call')
|
||||||
emit('reinstall')
|
emit('reinstall')
|
||||||
@@ -571,7 +580,7 @@ provideInstallationSettings({
|
|||||||
content_variant: 'modpack',
|
content_variant: 'modpack',
|
||||||
spec: {
|
spec: {
|
||||||
platform: 'modrinth',
|
platform: 'modrinth',
|
||||||
project_id: modpack.value.spec.project_id,
|
project_id: modpackProjectId.value,
|
||||||
version_id: version.id,
|
version_id: version.id,
|
||||||
},
|
},
|
||||||
soft_override: true,
|
soft_override: true,
|
||||||
@@ -590,18 +599,18 @@ provideInstallationSettings({
|
|||||||
|
|
||||||
updaterModalProps: computed(() => ({
|
updaterModalProps: computed(() => ({
|
||||||
isApp: false,
|
isApp: false,
|
||||||
currentVersionId: modpack.value?.spec.version_id ?? '',
|
currentVersionId:
|
||||||
|
modpack.value?.spec.platform === 'modrinth' ? modpack.value.spec.version_id : '',
|
||||||
projectIconUrl: modpack.value?.icon_url ?? undefined,
|
projectIconUrl: modpack.value?.icon_url ?? undefined,
|
||||||
projectName:
|
projectName:
|
||||||
modpack.value?.title ??
|
modpack.value?.title ?? modpackProjectId.value ?? formatMessage(commonMessages.modpackLabel),
|
||||||
modpack.value?.spec.project_id ??
|
|
||||||
formatMessage(commonMessages.modpackLabel),
|
|
||||||
currentGameVersion: addonsQuery.data.value?.game_version ?? server.value?.mc_version ?? '',
|
currentGameVersion: addonsQuery.data.value?.game_version ?? server.value?.mc_version ?? '',
|
||||||
currentLoader: addonsQuery.data.value?.modloader ?? server.value?.loader ?? '',
|
currentLoader: addonsQuery.data.value?.modloader ?? server.value?.loader ?? '',
|
||||||
})),
|
})),
|
||||||
|
|
||||||
isServer: true,
|
isServer: true,
|
||||||
isApp: false,
|
isApp: false,
|
||||||
|
showModpackVersionActions: computed(() => modpack.value?.spec.platform === 'modrinth'),
|
||||||
|
|
||||||
lockPlatform: true,
|
lockPlatform: true,
|
||||||
hideLoaderVersion: true,
|
hideLoaderVersion: true,
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ export namespace Archon {
|
|||||||
disabled: boolean
|
disabled: boolean
|
||||||
kind: AddonKind
|
kind: AddonKind
|
||||||
from_modpack: boolean
|
from_modpack: boolean
|
||||||
|
pack_client_retained: boolean
|
||||||
|
pack_client_depends: boolean
|
||||||
has_update: string | null
|
has_update: string | null
|
||||||
name: string | null
|
name: string | null
|
||||||
project_id: string | null
|
project_id: string | null
|
||||||
@@ -68,12 +70,21 @@ export namespace Archon {
|
|||||||
| 'purpur'
|
| 'purpur'
|
||||||
| 'vanilla'
|
| 'vanilla'
|
||||||
|
|
||||||
export type ModpackSpec = {
|
export type ModpackSpecModrinth = {
|
||||||
platform: 'modrinth'
|
platform: 'modrinth'
|
||||||
project_id: string
|
project_id: string
|
||||||
version_id: string
|
version_id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ModpackSpecLocalFile = {
|
||||||
|
platform: 'local_file'
|
||||||
|
filename: string
|
||||||
|
name: string
|
||||||
|
description: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModpackSpec = ModpackSpecModrinth | ModpackSpecLocalFile
|
||||||
|
|
||||||
export type ModpackOwner = {
|
export type ModpackOwner = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex gap-1.5 items-center justify-between px-3 pr-1.5 py-1.5 rounded-2xl bg-bg">
|
<div class="flex gap-1.5 items-center justify-between px-3 pr-1.5 py-1.5 rounded-2xl bg-bg">
|
||||||
<div class="grid grid-cols-[auto_1fr] gap-1.5 items-center">
|
<div class="grid grid-cols-[auto_1fr] gap-1.5 items-center">
|
||||||
<Avatar :src="icon" size="34px" class="!rounded-xl !shadow-none" />
|
<Avatar :src="icon" size="34px" class="!rounded-xl !shadow-none" raised />
|
||||||
<div class="flex flex-col items-start overflow-hidden">
|
<div class="flex flex-col items-start overflow-hidden">
|
||||||
<div
|
<div
|
||||||
v-tooltip="showCustomModpackTooltip ? formatMessage(messages.customModpackTooltip) : name"
|
v-tooltip="showCustomModpackTooltip ? formatMessage(messages.customModpackTooltip) : name"
|
||||||
@@ -12,6 +12,14 @@
|
|||||||
{{ name }}
|
{{ name }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
v-if="filename"
|
||||||
|
v-tooltip="filename"
|
||||||
|
class="truncate text-sm text-secondary max-w-full"
|
||||||
|
>
|
||||||
|
{{ filename }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="versionNumber"
|
||||||
v-tooltip="versionNumber"
|
v-tooltip="versionNumber"
|
||||||
class="truncate font-medium text-sm max-w-full"
|
class="truncate font-medium text-sm max-w-full"
|
||||||
:class="onclickVersion ? 'hover:underline cursor-pointer' : ''"
|
:class="onclickVersion ? 'hover:underline cursor-pointer' : ''"
|
||||||
@@ -38,7 +46,8 @@ import ButtonStyled from '../../base/ButtonStyled.vue'
|
|||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
name: string
|
name: string
|
||||||
versionNumber: string
|
versionNumber?: string
|
||||||
|
filename?: string
|
||||||
icon?: string
|
icon?: string
|
||||||
onclickName?: () => void
|
onclickName?: () => void
|
||||||
onclickVersion?: () => void
|
onclickVersion?: () => void
|
||||||
|
|||||||
@@ -25,7 +25,12 @@ import { useVIntl } from '#ui/composables/i18n'
|
|||||||
import { commonMessages } from '#ui/utils/common-messages'
|
import { commonMessages } from '#ui/utils/common-messages'
|
||||||
import { truncatedTooltip } from '#ui/utils/truncate'
|
import { truncatedTooltip } from '#ui/utils/truncate'
|
||||||
|
|
||||||
import type { ContentCardProject, ContentCardVersion, ContentOwner } from '../types'
|
import type {
|
||||||
|
ClientWarningType,
|
||||||
|
ContentCardProject,
|
||||||
|
ContentCardVersion,
|
||||||
|
ContentOwner,
|
||||||
|
} from '../types'
|
||||||
|
|
||||||
const { formatMessage } = useVIntl()
|
const { formatMessage } = useVIntl()
|
||||||
|
|
||||||
@@ -39,6 +44,8 @@ interface Props {
|
|||||||
installing?: boolean
|
installing?: boolean
|
||||||
hasUpdate?: boolean
|
hasUpdate?: boolean
|
||||||
isClientOnly?: boolean
|
isClientOnly?: boolean
|
||||||
|
clientWarning?: ClientWarningType | null
|
||||||
|
hideSwitchVersion?: boolean
|
||||||
overflowOptions?: OverflowMenuOption[]
|
overflowOptions?: OverflowMenuOption[]
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
showCheckbox?: boolean
|
showCheckbox?: boolean
|
||||||
@@ -55,6 +62,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
installing: false,
|
installing: false,
|
||||||
hasUpdate: false,
|
hasUpdate: false,
|
||||||
isClientOnly: false,
|
isClientOnly: false,
|
||||||
|
clientWarning: null,
|
||||||
|
hideSwitchVersion: false,
|
||||||
overflowOptions: undefined,
|
overflowOptions: undefined,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
showCheckbox: false,
|
showCheckbox: false,
|
||||||
@@ -83,6 +92,17 @@ const fileNameRef = ref<HTMLElement | null>(null)
|
|||||||
|
|
||||||
const isDisabled = computed(() => props.disabled || props.installing)
|
const isDisabled = computed(() => props.disabled || props.installing)
|
||||||
|
|
||||||
|
const clientWarningMessage = computed(() => {
|
||||||
|
switch (props.clientWarning) {
|
||||||
|
case 'retained':
|
||||||
|
return commonMessages.clientRetainedWarning
|
||||||
|
case 'depends':
|
||||||
|
return commonMessages.clientDependsWarning
|
||||||
|
default:
|
||||||
|
return commonMessages.clientOnlyWarning
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const { shift: shiftHeld } = useMagicKeys()
|
const { shift: shiftHeld } = useMagicKeys()
|
||||||
const deleteHovered = ref(false)
|
const deleteHovered = ref(false)
|
||||||
</script>
|
</script>
|
||||||
@@ -147,7 +167,7 @@ const deleteHovered = ref(false)
|
|||||||
<TriangleAlertIcon class="size-4 shrink-0 text-orange" />
|
<TriangleAlertIcon class="size-4 shrink-0 text-orange" />
|
||||||
<template #popper>
|
<template #popper>
|
||||||
<div class="max-w-[18rem] text-sm">
|
<div class="max-w-[18rem] text-sm">
|
||||||
{{ formatMessage(commonMessages.clientOnlyWarning) }}
|
{{ formatMessage(clientWarningMessage) }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -260,7 +280,11 @@ const deleteHovered = ref(false)
|
|||||||
<DownloadIcon class="size-5" />
|
<DownloadIcon class="size-5" />
|
||||||
</button>
|
</button>
|
||||||
</ButtonStyled>
|
</ButtonStyled>
|
||||||
<ButtonStyled v-else-if="hasSwitchVersionListener && version" circular type="transparent">
|
<ButtonStyled
|
||||||
|
v-else-if="hasSwitchVersionListener && version && !hideSwitchVersion"
|
||||||
|
circular
|
||||||
|
type="transparent"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
v-tooltip="formatMessage(commonMessages.switchVersionButton)"
|
v-tooltip="formatMessage(commonMessages.switchVersionButton)"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
|
|||||||
@@ -276,6 +276,8 @@ function handleSort(column: ContentCardTableSortColumn) {
|
|||||||
:installing="item.installing"
|
:installing="item.installing"
|
||||||
:has-update="item.hasUpdate"
|
:has-update="item.hasUpdate"
|
||||||
:is-client-only="item.isClientOnly"
|
:is-client-only="item.isClientOnly"
|
||||||
|
:client-warning="item.clientWarning"
|
||||||
|
:hide-switch-version="item.hideSwitchVersion"
|
||||||
:overflow-options="item.overflowOptions"
|
:overflow-options="item.overflowOptions"
|
||||||
:disabled="item.disabled"
|
:disabled="item.disabled"
|
||||||
:show-checkbox="showSelection"
|
:show-checkbox="showSelection"
|
||||||
@@ -329,6 +331,8 @@ function handleSort(column: ContentCardTableSortColumn) {
|
|||||||
:enabled="item.enabled"
|
:enabled="item.enabled"
|
||||||
:installing="item.installing"
|
:installing="item.installing"
|
||||||
:has-update="item.hasUpdate"
|
:has-update="item.hasUpdate"
|
||||||
|
:is-client-only="item.isClientOnly"
|
||||||
|
:client-warning="item.clientWarning"
|
||||||
:overflow-options="item.overflowOptions"
|
:overflow-options="item.overflowOptions"
|
||||||
:disabled="item.disabled"
|
:disabled="item.disabled"
|
||||||
:show-checkbox="showSelection"
|
:show-checkbox="showSelection"
|
||||||
|
|||||||
@@ -138,19 +138,28 @@ onUnmounted(() => {
|
|||||||
class="@container flex flex-col gap-4 rounded-[20px] bg-bg-raised p-6 shadow-md"
|
class="@container flex flex-col gap-4 rounded-[20px] bg-bg-raised p-6 shadow-md"
|
||||||
:class="{ 'opacity-50': disabled }"
|
:class="{ 'opacity-50': disabled }"
|
||||||
>
|
>
|
||||||
<div class="flex flex-wrap items-start justify-between gap-4">
|
<div class="flex flex-wrap items-center justify-between gap-4">
|
||||||
<div class="flex min-w-0 flex-1 items-start gap-4">
|
<div class="flex min-w-0 flex-1 items-center gap-4">
|
||||||
<AutoLink :to="projectLink" class="shrink-0">
|
<AutoLink :to="projectLink" class="shrink-0">
|
||||||
<Avatar :src="project.icon_url" :alt="project.title" size="5rem" no-shadow raised />
|
<Avatar :src="project.icon_url" :alt="project.title" size="5rem" no-shadow raised />
|
||||||
</AutoLink>
|
</AutoLink>
|
||||||
<div class="flex flex-col gap-1.5">
|
<div class="flex min-w-0 flex-col gap-1.5">
|
||||||
<AutoLink
|
<div class="flex min-w-0 flex-col">
|
||||||
:to="projectLink"
|
<AutoLink
|
||||||
class="text-xl font-semibold leading-8 text-contrast hover:underline"
|
:to="projectLink"
|
||||||
|
class="truncate text-xl font-semibold text-contrast"
|
||||||
|
:class="projectLink ? 'hover:underline' : ''"
|
||||||
|
>
|
||||||
|
{{ project.title }}
|
||||||
|
</AutoLink>
|
||||||
|
<span v-if="project.filename" class="truncate text-secondary mb-2">
|
||||||
|
{{ project.filename }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="owner || version"
|
||||||
|
class="flex flex-nowrap items-center gap-2 overflow-hidden text-secondary"
|
||||||
>
|
>
|
||||||
{{ project.title }}
|
|
||||||
</AutoLink>
|
|
||||||
<div class="flex flex-nowrap items-center gap-2 overflow-hidden text-secondary">
|
|
||||||
<AutoLink
|
<AutoLink
|
||||||
v-if="owner"
|
v-if="owner"
|
||||||
:to="owner.link"
|
:to="owner.link"
|
||||||
@@ -346,13 +355,16 @@ onUnmounted(() => {
|
|||||||
{{ project.description }}
|
{{ project.description }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-3">
|
<div
|
||||||
<div v-if="project.downloads !== undefined" class="flex items-center gap-2 text-secondary">
|
v-if="project.downloads != null || project.followers != null || categories?.length"
|
||||||
|
class="flex flex-wrap items-center gap-3"
|
||||||
|
>
|
||||||
|
<div v-if="project.downloads != null" class="flex items-center gap-2 text-secondary">
|
||||||
<DownloadIcon class="size-5" />
|
<DownloadIcon class="size-5" />
|
||||||
<span class="font-medium">{{ formatCompact(project.downloads) }}</span>
|
<span class="font-medium">{{ formatCompact(project.downloads) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="project.followers !== undefined" class="flex items-center gap-2 text-secondary">
|
<div v-if="project.followers != null" class="flex items-center gap-2 text-secondary">
|
||||||
<HeartIcon class="size-5" />
|
<HeartIcon class="size-5" />
|
||||||
<span class="font-medium">{{ formatCompact(project.followers) }}</span>
|
<span class="font-medium">{{ formatCompact(project.followers) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
normalizeProjectType,
|
normalizeProjectType,
|
||||||
} from '#ui/utils/common-messages'
|
} from '#ui/utils/common-messages'
|
||||||
|
|
||||||
import { isClientOnlyEnvironment } from '../../composables/content-filtering'
|
import { getClientWarningType, isClientOnlyEnvironment } from '../../composables/content-filtering'
|
||||||
import type { ContentCardTableItem, ContentItem } from '../../types'
|
import type { ContentCardTableItem, ContentItem } from '../../types'
|
||||||
import ContentCardTable from '../ContentCardTable.vue'
|
import ContentCardTable from '../ContentCardTable.vue'
|
||||||
import ContentSelectionBar from '../ContentSelectionBar.vue'
|
import ContentSelectionBar from '../ContentSelectionBar.vue'
|
||||||
@@ -239,7 +239,11 @@ const tableItems = computed<ContentCardTableItem[]>(() =>
|
|||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
...(props.enableToggle ? { enabled: item.enabled } : {}),
|
...(props.enableToggle ? { enabled: item.enabled } : {}),
|
||||||
isClientOnly: isClientOnlyEnvironment(item.environment),
|
isClientOnly:
|
||||||
|
isClientOnlyEnvironment(item.environment) ||
|
||||||
|
!!item.pack_client_retained ||
|
||||||
|
!!item.pack_client_depends,
|
||||||
|
clientWarning: getClientWarningType(item),
|
||||||
disabled: disabledIds.value.has(item.file_name),
|
disabled: disabledIds.value.has(item.file_name),
|
||||||
overflowOptions: [
|
overflowOptions: [
|
||||||
...(props.switchVersion
|
...(props.switchVersion
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { computed, ref, watch } from 'vue'
|
|||||||
import { useVIntl } from '#ui/composables/i18n'
|
import { useVIntl } from '#ui/composables/i18n'
|
||||||
import { commonProjectTypeCategoryMessages, normalizeProjectType } from '#ui/utils/common-messages'
|
import { commonProjectTypeCategoryMessages, normalizeProjectType } from '#ui/utils/common-messages'
|
||||||
|
|
||||||
import type { ContentItem } from '../types'
|
import type { ClientWarningType, ContentItem } from '../types'
|
||||||
|
|
||||||
const CLIENT_ONLY_ENVIRONMENTS = new Set(['client_only', 'singleplayer_only'])
|
const CLIENT_ONLY_ENVIRONMENTS = new Set(['client_only', 'singleplayer_only'])
|
||||||
|
|
||||||
@@ -13,6 +13,13 @@ export function isClientOnlyEnvironment(env?: string | null): boolean {
|
|||||||
return !!env && CLIENT_ONLY_ENVIRONMENTS.has(env)
|
return !!env && CLIENT_ONLY_ENVIRONMENTS.has(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getClientWarningType(item: ContentItem): ClientWarningType | null {
|
||||||
|
if (item.pack_client_retained) return 'retained'
|
||||||
|
if (item.pack_client_depends) return 'depends'
|
||||||
|
if (isClientOnlyEnvironment(item.environment)) return 'environment'
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
export interface ContentFilterOption {
|
export interface ContentFilterOption {
|
||||||
id: string
|
id: string
|
||||||
label: string
|
label: string
|
||||||
@@ -55,10 +62,7 @@ export function useContentFilters(items: Ref<ContentItem[]>, config?: ContentFil
|
|||||||
options.push({ id: 'updates', label: 'Updates' })
|
options.push({ id: 'updates', label: 'Updates' })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (config?.showClientOnlyFilter && items.value.some((m) => getClientWarningType(m) !== null)) {
|
||||||
config?.showClientOnlyFilter &&
|
|
||||||
items.value.some((m) => isClientOnlyEnvironment(m.environment))
|
|
||||||
) {
|
|
||||||
options.push({ id: 'client-only', label: 'Client-only' })
|
options.push({ id: 'client-only', label: 'Client-only' })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +106,7 @@ export function useContentFilters(items: Ref<ContentItem[]>, config?: ContentFil
|
|||||||
for (const filter of activeAttributes) {
|
for (const filter of activeAttributes) {
|
||||||
if (filter === 'updates' && !item.has_update) return false
|
if (filter === 'updates' && !item.has_update) return false
|
||||||
if (filter === 'disabled' && item.enabled) return false
|
if (filter === 'disabled' && item.enabled) return false
|
||||||
if (filter === 'client-only' && !isClientOnlyEnvironment(item.environment)) return false
|
if (filter === 'client-only' && getClientWarningType(item) === null) return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import ConfirmBulkUpdateModal from './components/modals/ConfirmBulkUpdateModal.v
|
|||||||
import ConfirmDeletionModal from './components/modals/ConfirmDeletionModal.vue'
|
import ConfirmDeletionModal from './components/modals/ConfirmDeletionModal.vue'
|
||||||
import ConfirmUnlinkModal from './components/modals/ConfirmUnlinkModal.vue'
|
import ConfirmUnlinkModal from './components/modals/ConfirmUnlinkModal.vue'
|
||||||
import {
|
import {
|
||||||
|
getClientWarningType,
|
||||||
isClientOnlyEnvironment,
|
isClientOnlyEnvironment,
|
||||||
useBulkOperation,
|
useBulkOperation,
|
||||||
useChangingItems,
|
useChangingItems,
|
||||||
@@ -279,7 +280,12 @@ const tableItems = computed<ContentCardTableItem[]>(() => {
|
|||||||
item.installing === true,
|
item.installing === true,
|
||||||
installing: item.installing === true,
|
installing: item.installing === true,
|
||||||
hasUpdate: item.has_update,
|
hasUpdate: item.has_update,
|
||||||
isClientOnly: isClientOnlyEnvironment(item.environment),
|
isClientOnly:
|
||||||
|
isClientOnlyEnvironment(item.environment) ||
|
||||||
|
!!item.pack_client_retained ||
|
||||||
|
!!item.pack_client_depends,
|
||||||
|
clientWarning: getClientWarningType(item),
|
||||||
|
hideSwitchVersion: !base.versionLink,
|
||||||
overflowOptions: ctx.getOverflowOptions?.(item),
|
overflowOptions: ctx.getOverflowOptions?.(item),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ export interface ContentOwner {
|
|||||||
link?: string | RouteLocationRaw | (() => void)
|
link?: string | RouteLocationRaw | (() => void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ClientWarningType = 'retained' | 'depends' | 'environment'
|
||||||
|
|
||||||
export interface ContentCardTableItem {
|
export interface ContentCardTableItem {
|
||||||
id: string
|
id: string
|
||||||
project: ContentCardProject
|
project: ContentCardProject
|
||||||
@@ -33,6 +35,8 @@ export interface ContentCardTableItem {
|
|||||||
installing?: boolean
|
installing?: boolean
|
||||||
hasUpdate?: boolean
|
hasUpdate?: boolean
|
||||||
isClientOnly?: boolean
|
isClientOnly?: boolean
|
||||||
|
clientWarning?: ClientWarningType | null
|
||||||
|
hideSwitchVersion?: boolean
|
||||||
overflowOptions?: OverflowMenuOption[]
|
overflowOptions?: OverflowMenuOption[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,13 +57,19 @@ export interface ContentItem extends Omit<
|
|||||||
update_version_id: string | null
|
update_version_id: string | null
|
||||||
date_added?: string
|
date_added?: string
|
||||||
environment?: string
|
environment?: string
|
||||||
|
pack_client_retained?: boolean
|
||||||
|
pack_client_depends?: boolean
|
||||||
installing?: boolean
|
installing?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ContentModpackCardProject = Pick<
|
export type ContentModpackCardProject = Pick<
|
||||||
Labrinth.Projects.v2.Project,
|
Labrinth.Projects.v2.Project,
|
||||||
'id' | 'slug' | 'title' | 'icon_url' | 'description' | 'downloads' | 'followers'
|
'id' | 'slug' | 'title' | 'icon_url' | 'description'
|
||||||
>
|
> & {
|
||||||
|
downloads?: number | null
|
||||||
|
followers?: number | null
|
||||||
|
filename?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
export type ContentModpackCardVersion = Pick<
|
export type ContentModpackCardVersion = Pick<
|
||||||
Labrinth.Versions.v2.Version,
|
Labrinth.Versions.v2.Version,
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ const diffTypeMessages = defineMessages({
|
|||||||
},
|
},
|
||||||
removed: {
|
removed: {
|
||||||
id: 'content.diff-modal.diff-type.removed',
|
id: 'content.diff-modal.diff-type.removed',
|
||||||
defaultMessage: 'Removed',
|
defaultMessage: 'Disabled',
|
||||||
},
|
},
|
||||||
updated: {
|
updated: {
|
||||||
id: 'content.diff-modal.diff-type.updated',
|
id: 'content.diff-modal.diff-type.updated',
|
||||||
|
|||||||
@@ -86,7 +86,11 @@ const disabledPlatforms = computed(() => {
|
|||||||
return ctx.availablePlatforms.filter((p) => p !== ctx.currentPlatform.value)
|
return ctx.availablePlatforms.filter((p) => p !== ctx.currentPlatform.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const showModpackVersionActions = ctx.showModpackVersionActions ?? true
|
const showModpackVersionActions = computed(() => {
|
||||||
|
const val = ctx.showModpackVersionActions
|
||||||
|
if (val == null) return true
|
||||||
|
return typeof val === 'boolean' ? val : val.value
|
||||||
|
})
|
||||||
|
|
||||||
function handleModpackUpdateRequest(version: Labrinth.Versions.v2.Version, event?: MouseEvent) {
|
function handleModpackUpdateRequest(version: Labrinth.Versions.v2.Version, event?: MouseEvent) {
|
||||||
pendingUpdateVersion.value = version
|
pendingUpdateVersion.value = version
|
||||||
@@ -284,26 +288,31 @@ const messages = defineMessages({
|
|||||||
class="flex items-center gap-2.5 rounded-[20px] bg-surface-2 p-3"
|
class="flex items-center gap-2.5 rounded-[20px] bg-surface-2 p-3"
|
||||||
>
|
>
|
||||||
<AutoLink :to="ctx.modpack.value.link" class="shrink-0">
|
<AutoLink :to="ctx.modpack.value.link" class="shrink-0">
|
||||||
<div
|
<Avatar
|
||||||
class="size-14 shrink-0 overflow-hidden rounded-2xl border border-solid border-surface-5"
|
:src="ctx.modpack.value.iconUrl"
|
||||||
>
|
:alt="ctx.modpack.value.title"
|
||||||
<Avatar
|
size="3.5rem"
|
||||||
v-if="ctx.modpack.value.iconUrl"
|
no-shadow
|
||||||
:src="ctx.modpack.value.iconUrl"
|
raised
|
||||||
:alt="ctx.modpack.value.title"
|
/>
|
||||||
size="100%"
|
|
||||||
no-shadow
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</AutoLink>
|
</AutoLink>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex min-w-0 flex-col gap-1">
|
||||||
<AutoLink
|
<div class="flex min-w-0 flex-col">
|
||||||
:to="ctx.modpack.value.link"
|
<AutoLink
|
||||||
class="font-semibold text-contrast hover:underline"
|
:to="ctx.modpack.value.link"
|
||||||
|
class="truncate font-semibold text-contrast"
|
||||||
|
:class="ctx.modpack.value.link ? 'hover:underline' : ''"
|
||||||
|
>
|
||||||
|
{{ ctx.modpack.value.title }}
|
||||||
|
</AutoLink>
|
||||||
|
<span v-if="ctx.modpack.value.filename" class="truncate text-sm text-secondary">
|
||||||
|
{{ ctx.modpack.value.filename }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="ctx.modpack.value.owner || ctx.modpack.value.versionNumber"
|
||||||
|
class="flex items-center gap-2 text-sm text-secondary"
|
||||||
>
|
>
|
||||||
{{ ctx.modpack.value.title }}
|
|
||||||
</AutoLink>
|
|
||||||
<div class="flex items-center gap-2 text-sm text-secondary">
|
|
||||||
<AutoLink
|
<AutoLink
|
||||||
v-if="ctx.modpack.value.owner"
|
v-if="ctx.modpack.value.owner"
|
||||||
:to="
|
:to="
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export interface InstallationSettingsContext {
|
|||||||
isApp: boolean
|
isApp: boolean
|
||||||
|
|
||||||
/** When false, hides change-version and reinstall buttons in linked state (default: true) */
|
/** When false, hides change-version and reinstall buttons in linked state (default: true) */
|
||||||
showModpackVersionActions?: boolean
|
showModpackVersionActions?: boolean | ComputedRef<boolean>
|
||||||
|
|
||||||
repairing?: Ref<boolean>
|
repairing?: Ref<boolean>
|
||||||
reinstalling?: Ref<boolean>
|
reinstalling?: Ref<boolean>
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ export interface InstallationModpackOwner {
|
|||||||
export interface InstallationModpackData {
|
export interface InstallationModpackData {
|
||||||
iconUrl?: string
|
iconUrl?: string
|
||||||
title: string
|
title: string
|
||||||
link: string | RouteLocationRaw
|
link?: string | RouteLocationRaw
|
||||||
versionNumber?: string
|
versionNumber?: string
|
||||||
|
filename?: string
|
||||||
owner?: InstallationModpackOwner
|
owner?: InstallationModpackOwner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,10 @@ const contentQuery = useQuery({
|
|||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
const modpackProjectId = computed(() => contentQuery.data.value?.modpack?.spec.project_id ?? null)
|
const modpackProjectId = computed(() => {
|
||||||
|
const spec = contentQuery.data.value?.modpack?.spec
|
||||||
|
return spec?.platform === 'modrinth' ? spec.project_id : null
|
||||||
|
})
|
||||||
|
|
||||||
const modpackVersionsQuery = useQuery({
|
const modpackVersionsQuery = useQuery({
|
||||||
queryKey: computed(() => ['labrinth', 'versions', 'v2', modpackProjectId.value]),
|
queryKey: computed(() => ['labrinth', 'versions', 'v2', modpackProjectId.value]),
|
||||||
@@ -146,24 +149,32 @@ const projectQuery = useQuery({
|
|||||||
const modpack = computed<ContentModpackData | null>(() => {
|
const modpack = computed<ContentModpackData | null>(() => {
|
||||||
const mp = contentQuery.data.value?.modpack
|
const mp = contentQuery.data.value?.modpack
|
||||||
if (!mp) return null
|
if (!mp) return null
|
||||||
|
const isLocal = mp.spec.platform === 'local_file'
|
||||||
const project = projectQuery.data.value
|
const project = projectQuery.data.value
|
||||||
|
const projectId = isLocal ? null : mp.spec.project_id
|
||||||
return {
|
return {
|
||||||
project: {
|
project: {
|
||||||
id: mp.spec.project_id,
|
id: projectId ?? mp.title ?? '',
|
||||||
slug: project?.slug ?? mp.spec.project_id,
|
slug: project?.slug ?? projectId ?? '',
|
||||||
title: mp.title ?? mp.spec.project_id,
|
title: mp.title ?? (isLocal ? mp.spec.name : projectId) ?? '',
|
||||||
icon_url: mp.icon_url ?? undefined,
|
icon_url: mp.icon_url ?? undefined,
|
||||||
description: mp.description ?? '',
|
description: mp.description ?? '',
|
||||||
downloads: mp.downloads ?? 0,
|
downloads: mp.downloads,
|
||||||
followers: mp.followers ?? 0,
|
followers: mp.followers,
|
||||||
|
filename: isLocal ? mp.spec.filename : undefined,
|
||||||
} as ContentModpackCardProject,
|
} as ContentModpackCardProject,
|
||||||
projectLink: `/project/${project?.slug ?? mp.spec.project_id}`,
|
projectLink: projectId ? `/project/${project?.slug ?? projectId}` : undefined,
|
||||||
version: {
|
version: isLocal
|
||||||
id: mp.spec.version_id,
|
? undefined
|
||||||
version_number: mp.version_number ?? '',
|
: ({
|
||||||
date_published: mp.date_published ?? '',
|
id: mp.spec.version_id,
|
||||||
} as ContentModpackCardVersion,
|
version_number: mp.version_number ?? '',
|
||||||
versionLink: `/project/${project?.slug ?? mp.spec.project_id}/version/${mp.spec.version_id}`,
|
date_published: mp.date_published ?? '',
|
||||||
|
} as ContentModpackCardVersion),
|
||||||
|
versionLink:
|
||||||
|
projectId && !isLocal
|
||||||
|
? `/project/${project?.slug ?? projectId}/version/${mp.spec.version_id}`
|
||||||
|
: undefined,
|
||||||
owner: mp.owner
|
owner: mp.owner
|
||||||
? {
|
? {
|
||||||
id: mp.owner.id,
|
id: mp.owner.id,
|
||||||
@@ -499,6 +510,8 @@ function addonToContentItem(addon: Archon.Content.v1.Addon): ContentItem {
|
|||||||
has_update: !!addon.has_update,
|
has_update: !!addon.has_update,
|
||||||
update_version_id: addon.has_update,
|
update_version_id: addon.has_update,
|
||||||
environment: addon.version?.environment ?? undefined,
|
environment: addon.version?.environment ?? undefined,
|
||||||
|
pack_client_retained: addon.pack_client_retained,
|
||||||
|
pack_client_depends: addon.pack_client_depends,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,7 +690,7 @@ async function handleSwitchVersion(item: ContentItem) {
|
|||||||
|
|
||||||
async function handleModpackUpdate() {
|
async function handleModpackUpdate() {
|
||||||
const mp = contentQuery.data.value?.modpack
|
const mp = contentQuery.data.value?.modpack
|
||||||
if (!mp?.spec.project_id) return
|
if (!mp || mp.spec.platform !== 'modrinth') return
|
||||||
|
|
||||||
updatingModpack.value = true
|
updatingModpack.value = true
|
||||||
updatingProject.value = null
|
updatingProject.value = null
|
||||||
@@ -767,7 +780,8 @@ function handleModalUpdate(selectedVersion: Labrinth.Versions.v2.Version, event?
|
|||||||
pendingModpackUpdateVersion.value = selectedVersion
|
pendingModpackUpdateVersion.value = selectedVersion
|
||||||
handleModpackUpdateConfirm()
|
handleModpackUpdateConfirm()
|
||||||
} else {
|
} else {
|
||||||
const currentVersionId = contentQuery.data.value?.modpack?.spec.version_id
|
const mpSpec = contentQuery.data.value?.modpack?.spec
|
||||||
|
const currentVersionId = mpSpec?.platform === 'modrinth' ? mpSpec.version_id : undefined
|
||||||
const currentVersion = updatingProjectVersions.value.find((v) => v.id === currentVersionId)
|
const currentVersion = updatingProjectVersions.value.find((v) => v.id === currentVersionId)
|
||||||
isModpackUpdateDowngrade.value = currentVersion
|
isModpackUpdateDowngrade.value = currentVersion
|
||||||
? new Date(selectedVersion.date_published) < new Date(currentVersion.date_published)
|
? new Date(selectedVersion.date_published) < new Date(currentVersion.date_published)
|
||||||
@@ -785,7 +799,7 @@ async function performUpdate(selectedVersion: Labrinth.Versions.v2.Version) {
|
|||||||
try {
|
try {
|
||||||
if (updatingModpack.value) {
|
if (updatingModpack.value) {
|
||||||
const mp = contentQuery.data.value?.modpack
|
const mp = contentQuery.data.value?.modpack
|
||||||
if (!mp) return
|
if (!mp || mp.spec.platform !== 'modrinth') return
|
||||||
await client.archon.content_v1.installContent(serverId, worldId.value!, {
|
await client.archon.content_v1.installContent(serverId, worldId.value!, {
|
||||||
content_variant: 'modpack',
|
content_variant: 'modpack',
|
||||||
spec: {
|
spec: {
|
||||||
@@ -895,13 +909,15 @@ provideContentManager({
|
|||||||
getOverflowOptions,
|
getOverflowOptions,
|
||||||
mapToTableItem: (item) => {
|
mapToTableItem: (item) => {
|
||||||
const projectType = item.project_type ?? type.value
|
const projectType = item.project_type ?? type.value
|
||||||
|
const addon = addonLookup.value.get(item.file_name)
|
||||||
|
const hasModrinthProject = !!addon?.project_id
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
project: item.project,
|
project: item.project,
|
||||||
projectLink: item.project?.id ? `/${projectType}/${item.project.id}` : undefined,
|
projectLink: hasModrinthProject ? `/${projectType}/${item.project.id}` : undefined,
|
||||||
version: item.version,
|
version: item.version,
|
||||||
versionLink:
|
versionLink:
|
||||||
item.project?.id && item.version?.id
|
hasModrinthProject && item.version?.id
|
||||||
? `/${projectType}/${item.project.id}/version/${item.version.id}`
|
? `/${projectType}/${item.project.id}/version/${item.version.id}`
|
||||||
: undefined,
|
: undefined,
|
||||||
owner: item.owner
|
owner: item.owner
|
||||||
@@ -935,7 +951,9 @@ provideContentManager({
|
|||||||
:current-loader="currentLoader"
|
:current-loader="currentLoader"
|
||||||
:current-version-id="
|
:current-version-id="
|
||||||
updatingModpack
|
updatingModpack
|
||||||
? (contentQuery.data.value?.modpack?.spec.version_id ?? '')
|
? contentQuery.data.value?.modpack?.spec.platform === 'modrinth'
|
||||||
|
? contentQuery.data.value.modpack.spec.version_id
|
||||||
|
: ''
|
||||||
: (updatingProject?.version?.id ?? '')
|
: (updatingProject?.version?.id ?? '')
|
||||||
"
|
"
|
||||||
:is-app="false"
|
:is-app="false"
|
||||||
|
|||||||
@@ -282,7 +282,7 @@
|
|||||||
"defaultMessage": "Added (dependency)"
|
"defaultMessage": "Added (dependency)"
|
||||||
},
|
},
|
||||||
"content.diff-modal.diff-type.removed": {
|
"content.diff-modal.diff-type.removed": {
|
||||||
"defaultMessage": "Removed"
|
"defaultMessage": "Disabled"
|
||||||
},
|
},
|
||||||
"content.diff-modal.diff-type.updated": {
|
"content.diff-modal.diff-type.updated": {
|
||||||
"defaultMessage": "Updated"
|
"defaultMessage": "Updated"
|
||||||
@@ -1196,9 +1196,15 @@
|
|||||||
"label.changes-saved": {
|
"label.changes-saved": {
|
||||||
"defaultMessage": "Changes saved"
|
"defaultMessage": "Changes saved"
|
||||||
},
|
},
|
||||||
|
"label.client-depends-warning": {
|
||||||
|
"defaultMessage": "This mod depends on a client-side mod and may cause issues when starting your server"
|
||||||
|
},
|
||||||
"label.client-only-warning": {
|
"label.client-only-warning": {
|
||||||
"defaultMessage": "This is a client-side mod and may cause issues. We've kept it enabled because some authors mislabel environments, and the loader should resolve the conflict."
|
"defaultMessage": "This is a client-side mod and may cause issues. We've kept it enabled because some authors mislabel environments, and the loader should resolve the conflict."
|
||||||
},
|
},
|
||||||
|
"label.client-retained-warning": {
|
||||||
|
"defaultMessage": "This is a client-side mod that was installed as a dependency and may cause issues when starting your server"
|
||||||
|
},
|
||||||
"label.collections": {
|
"label.collections": {
|
||||||
"defaultMessage": "Collections"
|
"defaultMessage": "Collections"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -406,6 +406,16 @@ export const commonMessages = defineMessages({
|
|||||||
defaultMessage:
|
defaultMessage:
|
||||||
"This is a client-side mod and may cause issues. We've kept it enabled because some authors mislabel environments, and the loader should resolve the conflict.",
|
"This is a client-side mod and may cause issues. We've kept it enabled because some authors mislabel environments, and the loader should resolve the conflict.",
|
||||||
},
|
},
|
||||||
|
clientRetainedWarning: {
|
||||||
|
id: 'label.client-retained-warning',
|
||||||
|
defaultMessage:
|
||||||
|
'This is a client-side mod that was installed as a dependency and may cause issues when starting your server',
|
||||||
|
},
|
||||||
|
clientDependsWarning: {
|
||||||
|
id: 'label.client-depends-warning',
|
||||||
|
defaultMessage:
|
||||||
|
'This mod depends on a client-side mod and may cause issues when starting your server',
|
||||||
|
},
|
||||||
selectAllLabel: {
|
selectAllLabel: {
|
||||||
id: 'label.select-all',
|
id: 'label.select-all',
|
||||||
defaultMessage: 'Select all',
|
defaultMessage: 'Select all',
|
||||||
|
|||||||
Reference in New Issue
Block a user