feat(SPA): media library indications for custom medial field type usage

This commit is contained in:
Florian Sylvain committed 2026-10-21 16:17:09 +02:00
1 parent 3e7fc9a4a5
commit 0fc3511e2b
28 files changed
+2060 -131

No files matched your search

+25 -15
View File
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { t } from '@/i18n'
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { computed, nextTick, onBeforeUnmount, provide, ref, watch } from 'vue'
import { useRoute, type LocationQuery } from 'vue-router'
import { useEventListener, useMediaQuery, useOnline } from '@vueuse/core'
import { useLocalizedToast } from '@/i18n/presentation'
@@ -30,7 +30,6 @@ import {
useMedia,
type BulkDeleteResult,
type MediaItem,
type MediaUsageItem,
} from '@/composables/useMedia'
import { useMediaSelection } from '@/composables/useMediaSelection'
import { useMediaUpload } from '@/composables/useMediaUpload'
@@ -39,6 +38,7 @@ import type { PurgeResult } from '@/composables/useMediaPurge'
import { toastsAboveBulkBar } from '@/composables/useToastOffset'
import { groupDuplicates, type DuplicateGroup } from '@/lib/mediaDuplicates'
import { joinNames } from '@/lib/mediaFormat'
import { MEDIA_USAGE_KEY } from '@/lib/mediaUsageContext'
import {
isFullSet,
parseMediaControls,
@@ -149,7 +149,6 @@ const purgeOpen = ref(false)
const dragDepth = ref(0)
const announcementSource = ref<() => string>(() => '')
const announcement = computed(() => announcementSource.value())
const usage = ref<MediaUsageItem[]>([])
const copies = ref<MediaItem[]>([])
let opener: HTMLElement | null = null
let purgeChangedList = false
@@ -160,9 +159,25 @@ let bulkFailureRevision = 0
let bulkFailureToastId = BULK_FAILED_TOAST_ID
let failureToastShown = false
provide(MEDIA_USAGE_KEY, {
stateOf: media.usageOf,
ensure: (id) => {
void media.ensureUsage(id)
},
})
const activeItem = computed(
() => media.items.value.find((item) => item.id === activeId.value) ?? null,
)
watch(
() =>
panelOpen.value && activeItem.value !== null && media.usageOf(activeItem.value.id) === null,
(missing) => {
if (missing && activeItem.value && activeItem.value.used_in > 0) {
loadUsage(activeItem.value)
}
},
)
const closingItem = ref<MediaItem | null>(null)
const panelItem = computed(() => activeItem.value ?? closingItem.value)
const counts = computed(() => (hasLoaded.value ? media.counts.value : null))
@@ -365,15 +380,10 @@ function restoreFocusToOpener(id: number | null) {
}
async function loadUsage(item: MediaItem) {
try {
const rows = await media.fetchUsage(item.id)
if (activeId.value === item.id) {
usage.value = rows
}
} catch (error) {
if (activeId.value === item.id && classifyMediaError(error) === 'not-found') {
handleGone(item)
}
await media.ensureUsage(item.id, true)
const state = media.usageOf(item.id)
if (activeId.value === item.id && state?.status === 'failed' && state.failure === 'not-found') {
handleGone(item)
}
}
@@ -395,7 +405,6 @@ function openPanel(id: number, element: HTMLElement) {
}
opener = element
activeId.value = id
usage.value = []
copies.value = []
skipFocusRestore = false
panelOpen.value = true
@@ -1150,7 +1159,7 @@ async function mergeAction() {
<MediaDetailPanel
:open="panelOpen"
:item="panelItem"
:usage="usage"
:usage="panelItem ? media.usageOf(panelItem.id) : null"
:copies="copies"
:offline="offline"
@update:open="onPanelOpenChange"
@@ -1158,6 +1167,7 @@ async function mergeAction() {
@rename="renameOpen = true"
@delete="deleteOpen = true"
@review-duplicates="reviewDuplicates"
@retry-usage="activeItem && loadUsage(activeItem)"
@gone="activeItem && handleGone(activeItem)"
/>
@@ -1171,7 +1181,7 @@ async function mergeAction() {
<DeleteMediaDialog
v-if="deleteOpen && activeItem"
:item="activeItem"
:usage="usage"
:usage="media.usageOf(activeItem.id)"
:action="deleteAction"
@close="deleteOpen = false"
/>