fix: app problems post release qa (#5554)
* fix: app problems post release qa * fix: lint * fix: dont prefill * fix: toggle gap * feat: macs thing * fix: lint
This commit is contained in:
@@ -4,9 +4,11 @@ import {
|
||||
MoreVerticalIcon,
|
||||
OrganizationIcon,
|
||||
SpinnerIcon,
|
||||
TrashExclamationIcon,
|
||||
TrashIcon,
|
||||
TriangleAlertIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { useMagicKeys } from '@vueuse/core'
|
||||
import { Tooltip } from 'floating-vue'
|
||||
import { computed, getCurrentInstance, ref } from 'vue'
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
@@ -64,7 +66,7 @@ const selected = defineModel<boolean>('selected')
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:enabled': [value: boolean]
|
||||
delete: []
|
||||
delete: [event: MouseEvent]
|
||||
update: []
|
||||
}>()
|
||||
|
||||
@@ -74,6 +76,9 @@ const hasUpdateListener = computed(() => typeof instance?.vnode.props?.onUpdate
|
||||
|
||||
const versionNumberRef = ref<HTMLElement | null>(null)
|
||||
const fileNameRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const { shift: shiftHeld } = useMagicKeys()
|
||||
const deleteHovered = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -84,9 +89,10 @@ const fileNameRef = ref<HTMLElement | null>(null)
|
||||
>
|
||||
<div
|
||||
class="flex min-w-0 items-center gap-4"
|
||||
:class="
|
||||
hideActions ? 'flex-1' : 'flex-1 @[800px]:w-[350px] @[800px]:shrink-0 @[800px]:flex-none'
|
||||
"
|
||||
:class="[
|
||||
hideActions ? 'flex-1' : 'flex-1 @[800px]:w-[350px] @[800px]:shrink-0 @[800px]:flex-none',
|
||||
enabled === false && !disabled ? 'grayscale opacity-50' : '',
|
||||
]"
|
||||
>
|
||||
<Checkbox
|
||||
v-if="showCheckbox"
|
||||
@@ -188,7 +194,10 @@ const fileNameRef = ref<HTMLElement | null>(null)
|
||||
|
||||
<div
|
||||
class="hidden flex-col gap-0.5 @[800px]:flex"
|
||||
:class="hideActions ? 'flex-1' : 'flex-1 min-w-0'"
|
||||
:class="[
|
||||
hideActions ? 'flex-1' : 'flex-1 min-w-0',
|
||||
enabled === false && !disabled ? 'grayscale opacity-50' : '',
|
||||
]"
|
||||
>
|
||||
<template v-if="version">
|
||||
<AutoLink
|
||||
@@ -221,7 +230,10 @@ const fileNameRef = ref<HTMLElement | null>(null)
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="!hideActions" class="flex min-w-[160px] shrink-0 items-center justify-end gap-2">
|
||||
<div
|
||||
v-if="!hideActions"
|
||||
class="flex min-w-[160px] shrink-0 items-center justify-end gap-2 transition-colors duration-200"
|
||||
>
|
||||
<slot name="additionalButtonsLeft" />
|
||||
|
||||
<!-- Fixed width container to reserve space for update button -->
|
||||
@@ -249,18 +261,34 @@ const fileNameRef = ref<HTMLElement | null>(null)
|
||||
:model-value="enabled"
|
||||
:disabled="disabled"
|
||||
:aria-label="project.title"
|
||||
small
|
||||
class="mr-2 my-auto"
|
||||
@update:model-value="(val) => emit('update:enabled', val as boolean)"
|
||||
/>
|
||||
|
||||
<ButtonStyled v-if="hasDeleteListener && !props.hideDelete" circular type="transparent">
|
||||
<button
|
||||
v-tooltip="formatMessage(commonMessages.deleteLabel)"
|
||||
v-tooltip="
|
||||
formatMessage(
|
||||
shiftHeld && deleteHovered
|
||||
? commonMessages.deleteImmediatelyLabel
|
||||
: commonMessages.deleteLabel,
|
||||
)
|
||||
"
|
||||
:disabled="disabled"
|
||||
@click="emit('delete')"
|
||||
@click="emit('delete', $event)"
|
||||
@mouseenter="deleteHovered = true"
|
||||
@mouseleave="deleteHovered = false"
|
||||
>
|
||||
<TrashIcon class="size-5 text-secondary" />
|
||||
<span class="relative size-5">
|
||||
<TrashIcon
|
||||
class="absolute inset-0 size-5 text-secondary transition-opacity duration-200"
|
||||
:class="shiftHeld && deleteHovered ? 'opacity-0' : 'opacity-100'"
|
||||
/>
|
||||
<TrashExclamationIcon
|
||||
class="absolute inset-0 size-5 text-red transition-opacity duration-200"
|
||||
:class="shiftHeld && deleteHovered ? 'opacity-100' : 'opacity-0'"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ const selectedIds = defineModel<string[]>('selectedIds', { default: () => [] })
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:enabled': [id: string, value: boolean]
|
||||
delete: [id: string]
|
||||
delete: [id: string, event: MouseEvent]
|
||||
update: [id: string]
|
||||
sort: [column: ContentCardTableSortColumn, direction: ContentCardTableSortDirection]
|
||||
}>()
|
||||
@@ -280,7 +280,11 @@ function handleSort(column: ContentCardTableSortColumn) {
|
||||
:hide-actions="!hasAnyActions"
|
||||
:selected="isItemSelected(item.id)"
|
||||
:class="[
|
||||
(visibleRange.start + idx) % 2 === 1 ? 'bg-surface-1.5' : 'bg-surface-2',
|
||||
isItemSelected(item.id)
|
||||
? 'bg-surface-2.5'
|
||||
: (visibleRange.start + idx) % 2 === 1
|
||||
? 'bg-surface-1.5'
|
||||
: 'bg-surface-2',
|
||||
'border-0 border-t border-solid border-surface-4',
|
||||
visibleRange.start + idx === items.length - 1 && !flat ? 'rounded-b-[20px]' : '',
|
||||
]"
|
||||
@@ -288,7 +292,7 @@ function handleSort(column: ContentCardTableSortColumn) {
|
||||
(val) => toggleItemSelection(item.id, val ?? false, visibleRange.start + idx)
|
||||
"
|
||||
@update:enabled="(val) => emit('update:enabled', item.id, val)"
|
||||
@delete="emit('delete', item.id)"
|
||||
@delete="(e: MouseEvent) => emit('delete', item.id, e)"
|
||||
@update="emit('update', item.id)"
|
||||
>
|
||||
<template #additionalButtonsLeft>
|
||||
@@ -326,13 +330,17 @@ function handleSort(column: ContentCardTableSortColumn) {
|
||||
:hide-actions="!hasAnyActions"
|
||||
:selected="isItemSelected(item.id)"
|
||||
:class="[
|
||||
index % 2 === 1 ? 'bg-surface-1.5' : 'bg-surface-2',
|
||||
isItemSelected(item.id)
|
||||
? 'bg-surface-2.5'
|
||||
: index % 2 === 1
|
||||
? 'bg-surface-1.5'
|
||||
: 'bg-surface-2',
|
||||
'border-0 border-t border-solid border-surface-4',
|
||||
index === items.length - 1 && !flat ? 'rounded-b-[20px]' : '',
|
||||
]"
|
||||
@update:selected="(val) => toggleItemSelection(item.id, val ?? false, index)"
|
||||
@update:enabled="(val) => emit('update:enabled', item.id, val)"
|
||||
@delete="emit('delete', item.id)"
|
||||
@delete="(e: MouseEvent) => emit('delete', item.id, e)"
|
||||
@update="emit('update', item.id)"
|
||||
>
|
||||
<template #additionalButtonsLeft>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<span class="text-lg font-extrabold text-contrast">{{
|
||||
header ??
|
||||
formatMessage(
|
||||
isModpack ? messages.switchModpackVersionHeader : messages.updateVersionHeader,
|
||||
isModpack.value ? messages.switchModpackVersionHeader : messages.updateVersionHeader,
|
||||
)
|
||||
}}</span>
|
||||
</template>
|
||||
@@ -246,10 +246,12 @@ import ButtonStyled from '#ui/components/base/ButtonStyled.vue'
|
||||
import StyledInput from '#ui/components/base/StyledInput.vue'
|
||||
import NewModal from '#ui/components/modal/NewModal.vue'
|
||||
import VersionChannelIndicator from '#ui/components/version/VersionChannelIndicator.vue'
|
||||
import { useDebugLogger } from '#ui/composables/debug-logger'
|
||||
import { defineMessages, useVIntl } from '#ui/composables/i18n'
|
||||
import { commonMessages } from '#ui/utils/common-messages'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const debug = useDebugLogger('ContentUpdaterModal')
|
||||
|
||||
const messages = defineMessages({
|
||||
updateVersionHeader: {
|
||||
@@ -326,8 +328,8 @@ const props = withDefaults(
|
||||
currentLoader: string
|
||||
currentVersionId: string
|
||||
isApp: boolean
|
||||
/** Whether this is a modpack update (changes header text) */
|
||||
isModpack?: boolean
|
||||
/** The project type (e.g. mod, shader, resourcepack, datapack, modpack). */
|
||||
projectType?: string
|
||||
projectIconUrl?: string
|
||||
projectName?: string
|
||||
header?: string
|
||||
@@ -337,7 +339,7 @@ const props = withDefaults(
|
||||
loadingChangelog?: boolean
|
||||
}>(),
|
||||
{
|
||||
isModpack: false,
|
||||
projectType: undefined,
|
||||
projectIconUrl: undefined,
|
||||
projectName: undefined,
|
||||
header: undefined,
|
||||
@@ -346,8 +348,10 @@ const props = withDefaults(
|
||||
},
|
||||
)
|
||||
|
||||
const isModpack = computed(() => props.projectType === 'modpack')
|
||||
|
||||
const emit = defineEmits<{
|
||||
update: [version: Labrinth.Versions.v2.Version]
|
||||
update: [version: Labrinth.Versions.v2.Version, event: MouseEvent]
|
||||
cancel: []
|
||||
/** Emitted when user selects a version, so parent can fetch full version data with changelog */
|
||||
versionSelect: [version: Labrinth.Versions.v2.Version]
|
||||
@@ -374,8 +378,20 @@ watch(
|
||||
|
||||
// Handle initial selection when versions first arrive
|
||||
if (newVersions.length > 0 && !selectedVersion.value && pendingInitialVersionId.value) {
|
||||
const version =
|
||||
newVersions.find((v) => v.id === pendingInitialVersionId.value) ?? newVersions[0]
|
||||
const pendingFound = newVersions.find((v) => v.id === pendingInitialVersionId.value)
|
||||
debug('versions watcher: initial selection', {
|
||||
pendingInitialVersionId: pendingInitialVersionId.value,
|
||||
foundPending: !!pendingFound,
|
||||
currentVersionId: props.currentVersionId,
|
||||
currentInList: newVersions.some((v) => v.id === props.currentVersionId),
|
||||
totalVersions: newVersions.length,
|
||||
loaderDistribution: [...new Set(newVersions.flatMap((v) => v.loaders))],
|
||||
gameVersionDistribution: [...new Set(newVersions.flatMap((v) => v.game_versions))].slice(
|
||||
0,
|
||||
10,
|
||||
),
|
||||
})
|
||||
const version = pendingFound ?? newVersions[0]
|
||||
selectedVersion.value = version
|
||||
if (version) {
|
||||
emit('versionSelect', version)
|
||||
@@ -386,12 +402,30 @@ watch(
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
const NON_MOD_PROJECT_TYPES = new Set(['shader', 'shaderpack', 'resourcepack', 'datapack'])
|
||||
|
||||
function isVersionCompatible(version: Labrinth.Versions.v2.Version): boolean {
|
||||
const hasGameVersion = version.game_versions.includes(props.currentGameVersion)
|
||||
const hasLoader = version.loaders.some(
|
||||
(loader) => loader.toLowerCase() === props.currentLoader.toLowerCase(),
|
||||
)
|
||||
return hasGameVersion && hasLoader
|
||||
const skipLoaderCheck = props.projectType != null && NON_MOD_PROJECT_TYPES.has(props.projectType)
|
||||
const hasLoader =
|
||||
skipLoaderCheck ||
|
||||
version.loaders.some((loader) => loader.toLowerCase() === props.currentLoader.toLowerCase())
|
||||
const compatible = hasGameVersion && hasLoader
|
||||
if (!compatible) {
|
||||
debug('isVersionCompatible: INCOMPATIBLE', {
|
||||
versionId: version.id,
|
||||
versionNumber: version.version_number,
|
||||
versionLoaders: version.loaders,
|
||||
versionGameVersions: version.game_versions,
|
||||
currentLoader: props.currentLoader,
|
||||
currentGameVersion: props.currentGameVersion,
|
||||
projectType: props.projectType,
|
||||
hasGameVersion,
|
||||
hasLoader,
|
||||
skipLoaderCheck,
|
||||
})
|
||||
}
|
||||
return compatible
|
||||
}
|
||||
|
||||
const currentVersion = computed(() => props.versions.find((v) => v.id === props.currentVersionId))
|
||||
@@ -413,10 +447,19 @@ const filteredVersions = computed(() => {
|
||||
)
|
||||
}
|
||||
|
||||
const beforeFilterCount = versions.length
|
||||
if (hideIncompatibleState.value) {
|
||||
versions = versions.filter(isVersionCompatible)
|
||||
}
|
||||
|
||||
debug('filteredVersions computed', {
|
||||
totalVersions: props.versions.length,
|
||||
afterSearchFilter: beforeFilterCount,
|
||||
afterCompatibilityFilter: versions.length,
|
||||
hiddenByCompatibility: beforeFilterCount - versions.length,
|
||||
hideIncompatible: hideIncompatibleState.value,
|
||||
})
|
||||
|
||||
return versions
|
||||
})
|
||||
|
||||
@@ -503,9 +546,9 @@ function handleVersionSelect(version: Labrinth.Versions.v2.Version) {
|
||||
emit('versionSelect', version)
|
||||
}
|
||||
|
||||
function handleUpdate() {
|
||||
function handleUpdate(event: MouseEvent) {
|
||||
if (selectedVersion.value) {
|
||||
emit('update', selectedVersion.value)
|
||||
emit('update', selectedVersion.value, event)
|
||||
hide()
|
||||
}
|
||||
}
|
||||
@@ -519,7 +562,23 @@ function show(initialVersionId?: string) {
|
||||
searchQuery.value = ''
|
||||
hideIncompatibleState.value = true
|
||||
|
||||
debug('show() called', {
|
||||
initialVersionId,
|
||||
currentVersionId: props.currentVersionId,
|
||||
currentGameVersion: props.currentGameVersion,
|
||||
currentLoader: props.currentLoader,
|
||||
projectType: props.projectType,
|
||||
versionsAvailable: props.versions.length,
|
||||
})
|
||||
|
||||
if (props.versions.length > 0) {
|
||||
const currentInList = props.versions.find((v) => v.id === props.currentVersionId)
|
||||
debug('show(): currentVersionId lookup', {
|
||||
currentVersionId: props.currentVersionId,
|
||||
foundInList: !!currentInList,
|
||||
allVersionIds: props.versions.map((v) => v.id),
|
||||
})
|
||||
|
||||
if (initialVersionId) {
|
||||
selectedVersion.value =
|
||||
props.versions.find((v) => v.id === initialVersionId) ?? props.versions[0]
|
||||
@@ -533,6 +592,9 @@ function show(initialVersionId?: string) {
|
||||
} else {
|
||||
selectedVersion.value = null
|
||||
pendingInitialVersionId.value = initialVersionId
|
||||
debug('show(): no versions yet, deferring selection', {
|
||||
pendingInitialVersionId: initialVersionId,
|
||||
})
|
||||
}
|
||||
|
||||
modal.value?.show()
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
class="size-5 shrink-0 text-brand-orange hover:brightness-110"
|
||||
/>
|
||||
</div>
|
||||
<span class="text-secondary">
|
||||
{{ formatMessage(messages.shiftClickHint) }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -111,5 +114,9 @@ const messages = defineMessages({
|
||||
defaultMessage:
|
||||
"A backup is in progress, it's recommended to wait for it to finish before performing this action.",
|
||||
},
|
||||
shiftClickHint: {
|
||||
id: 'content.inline-backup.shift-click-hint',
|
||||
defaultMessage: 'Hold Shift while clicking to skip confirmation.',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user