feat(SPA): media library indications for custom medial field type usage
This commit is contained in:
1 parent
3e7fc9a4a5
commit
0fc3511e2b
28 files changed
+2060
-131
No files matched your search
@@ -0,0 +1,140 @@
|
||||
import { t } from '@/i18n'
|
||||
import type { MediaUsageItem, UsageState } from '@/composables/useMedia'
|
||||
import { describeCell, plainCellText } from '@/lib/entryCells'
|
||||
import { pluralArticles } from '@/lib/mediaFormat'
|
||||
|
||||
const ENTRY_ROWS_SHOWN = 3
|
||||
const ARTICLE_TYPE = 'article'
|
||||
const ENTRY_TYPE = 'entry'
|
||||
|
||||
export interface UsageArticle {
|
||||
id: number
|
||||
title: string
|
||||
published: boolean
|
||||
}
|
||||
|
||||
export interface UsageEntry {
|
||||
id: number
|
||||
key: string
|
||||
typeName: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface UsageSplit {
|
||||
articles: UsageArticle[]
|
||||
entries: UsageEntry[]
|
||||
entryCount: number
|
||||
more: number
|
||||
}
|
||||
|
||||
export function pluralEntries(count: number) {
|
||||
return t(count === 1 ? '1 entry' : '{0} entries', [count])
|
||||
}
|
||||
|
||||
export function usageEntryTitle(item: MediaUsageItem) {
|
||||
const fallback = t('Entry {0}', [item.id])
|
||||
const first = item.first_field
|
||||
if (!first) {
|
||||
return fallback
|
||||
}
|
||||
const cell = describeCell(
|
||||
{
|
||||
key: first.key,
|
||||
label: first.key,
|
||||
type: first.type,
|
||||
required: false,
|
||||
unique: false,
|
||||
default: null,
|
||||
min: null,
|
||||
max: null,
|
||||
options: null,
|
||||
},
|
||||
first.value,
|
||||
)
|
||||
return cell.kind === 'empty' || cell.kind === 'thumbnail' ? fallback : plainCellText(cell)
|
||||
}
|
||||
|
||||
function toEntry(item: MediaUsageItem): UsageEntry {
|
||||
return {
|
||||
id: item.id,
|
||||
key: item.content_type_key ?? '',
|
||||
typeName: item.content_type_name ?? '',
|
||||
title: usageEntryTitle(item),
|
||||
}
|
||||
}
|
||||
|
||||
export function splitUsage(items: MediaUsageItem[]): UsageSplit {
|
||||
const articles = items
|
||||
.filter((item) => item.type === undefined || item.type === ARTICLE_TYPE)
|
||||
.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.title ?? '',
|
||||
published: item.published === true,
|
||||
}))
|
||||
const entryItems = items.filter((item) => item.type === ENTRY_TYPE)
|
||||
return {
|
||||
articles,
|
||||
entries: entryItems.slice(0, ENTRY_ROWS_SHOWN).map(toEntry),
|
||||
entryCount: entryItems.length,
|
||||
more: Math.max(0, entryItems.length - ENTRY_ROWS_SHOWN),
|
||||
}
|
||||
}
|
||||
|
||||
export function tooltipLines(items: MediaUsageItem[]) {
|
||||
const split = splitUsage(items)
|
||||
return [
|
||||
split.articles.length > 0 ? t('Used in articles: {0}', [split.articles.length]) : '',
|
||||
split.entryCount > 0 ? t('Used in entries: {0}', [split.entryCount]) : '',
|
||||
].filter(Boolean)
|
||||
}
|
||||
|
||||
export function usageSplitOf(state: UsageState | null) {
|
||||
return state?.status === 'ready' ? splitUsage(state.items) : null
|
||||
}
|
||||
|
||||
function inUseDescription(filename: string, split: UsageSplit | null) {
|
||||
const articles = split?.articles.length ?? 0
|
||||
const entries = split?.entryCount ?? 0
|
||||
if (articles > 0 && entries > 0) {
|
||||
return t(
|
||||
"\u0022{0}\u0022 is used in {1} and {2}. Those entry fields will show \u0022This image is no longer in the library\u0022, and the articles will show a broken image. This can't be undone.",
|
||||
[filename, pluralEntries(entries), pluralArticles(articles)],
|
||||
)
|
||||
}
|
||||
if (articles > 0) {
|
||||
return t(
|
||||
"\u0022{0}\u0022 is used in {1}. If you delete it, they will show a broken image. This can't be undone.",
|
||||
[filename, pluralArticles(articles)],
|
||||
)
|
||||
}
|
||||
if (entries > 0) {
|
||||
return t(
|
||||
"\u0022{0}\u0022 is used in {1}. Those entry fields will show \u0022This image is no longer in the library\u0022. This can't be undone.",
|
||||
[filename, pluralEntries(entries)],
|
||||
)
|
||||
}
|
||||
return t(
|
||||
"\u0022{0}\u0022 is in use. Articles will show a broken image and entry fields will show \u0022This image is no longer in the library\u0022. This can't be undone.",
|
||||
[filename],
|
||||
)
|
||||
}
|
||||
|
||||
export function deleteCopy(filename: string, usedIn: number, state: UsageState | null) {
|
||||
if (usedIn <= 0) {
|
||||
return {
|
||||
title: t('Delete this image?'),
|
||||
description: t(
|
||||
"\u0022{0}\u0022 will be deleted from your library. No article uses it. This can't be undone.",
|
||||
[filename],
|
||||
),
|
||||
split: null,
|
||||
}
|
||||
}
|
||||
const split = usageSplitOf(state)
|
||||
const known = split !== null && (split.articles.length > 0 || split.entryCount > 0)
|
||||
return {
|
||||
title: t("Delete an image that's in use?"),
|
||||
description: inUseDescription(filename, known ? split : null),
|
||||
split: known ? split : null,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user