feat: media duplicate purge, part 3 of 3: the SPA (Settings "Media cleanup" card, PurgeDuplicatesDialog, Media Duplicates toolbar button)
This commit is contained in:
1 parent
1a55a39425
commit
2303b12377
26 files changed
+3856
-83
No files matched your search
@@ -21,6 +21,7 @@ import MediaTable from '@/components/MediaTable.vue'
|
||||
import MediaToolbar from '@/components/MediaToolbar.vue'
|
||||
import MediaUploadPanel from '@/components/MediaUploadPanel.vue'
|
||||
import MergeMediaDialog from '@/components/MergeMediaDialog.vue'
|
||||
import PurgeDuplicatesDialog from '@/components/PurgeDuplicatesDialog.vue'
|
||||
import RenameMediaDialog from '@/components/RenameMediaDialog.vue'
|
||||
import { ApiError } from '@/composables/useApi'
|
||||
import {
|
||||
@@ -32,6 +33,7 @@ import {
|
||||
} from '@/composables/useMedia'
|
||||
import { useMediaSelection } from '@/composables/useMediaSelection'
|
||||
import { useMediaUpload } from '@/composables/useMediaUpload'
|
||||
import type { PurgeResult } from '@/composables/useMediaPurge'
|
||||
import { toastsAboveBulkBar } from '@/composables/useToastOffset'
|
||||
import { groupDuplicates, type DuplicateGroup } from '@/lib/mediaDuplicates'
|
||||
import { joinNames } from '@/lib/mediaFormat'
|
||||
@@ -41,7 +43,9 @@ import {
|
||||
toRouteQuery,
|
||||
type MediaControls,
|
||||
} from '@/lib/mediaQuery'
|
||||
import { purgedToast } from '@/lib/mediaPurge'
|
||||
import { RATE_LIMITED_TEXT, RATE_LIMITED_TITLE, UPLOAD_ACCEPT } from '@/lib/mediaUploadRows'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const TOAST_DURATION_MS = 5000
|
||||
@@ -71,6 +75,7 @@ const OFFLINE_COPY =
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const toast = useLocalizedToast()
|
||||
const session = useSessionStore()
|
||||
const media = useMedia()
|
||||
const selection = useMediaSelection(media.items)
|
||||
const upload = useMediaUpload({ onAdded: refreshInBackground })
|
||||
@@ -91,12 +96,14 @@ const deleteOpen = ref(false)
|
||||
const bulkDeleteOpen = ref(false)
|
||||
const bulkTargets = ref<MediaItem[]>([])
|
||||
const mergeTarget = ref<{ group: DuplicateGroup; keep: MediaItem } | null>(null)
|
||||
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
|
||||
let skipFocusRestore = false
|
||||
let pendingSelectId: number | null = null
|
||||
let bulkDeletedTotal = 0
|
||||
@@ -138,6 +145,12 @@ const filterLabel = computed(
|
||||
],
|
||||
)
|
||||
const showList = computed(() => ready.value && !nothingToShow.value && !emptyLibrary.value)
|
||||
const canPurge = computed(
|
||||
() => session.session?.role === 'owner' || session.session?.role === 'admin',
|
||||
)
|
||||
const showPurge = computed(
|
||||
() => canPurge.value && isDuplicates.value && showList.value && groups.value.length > 0,
|
||||
)
|
||||
const dragging = computed(() => dragDepth.value > 0)
|
||||
const uploadBlocked = computed(() => offline.value || upload.limited.value)
|
||||
const uploadLabel = computed(() =>
|
||||
@@ -150,6 +163,7 @@ const dialogOpen = computed(
|
||||
renameOpen.value ||
|
||||
deleteOpen.value ||
|
||||
bulkDeleteOpen.value ||
|
||||
purgeOpen.value ||
|
||||
mergeTarget.value !== null,
|
||||
)
|
||||
const hasSelection = computed(() => selection.count.value > 0)
|
||||
@@ -724,6 +738,33 @@ function openMerge(group: DuplicateGroup, keep: MediaItem) {
|
||||
mergeTarget.value = { group, keep }
|
||||
}
|
||||
|
||||
function openPurge(event?: Event) {
|
||||
if (offline.value) {
|
||||
return
|
||||
}
|
||||
opener = (event?.currentTarget as HTMLElement | null) ?? null
|
||||
purgeChangedList = false
|
||||
purgeOpen.value = true
|
||||
}
|
||||
|
||||
function onPurged(result: PurgeResult) {
|
||||
toast.add(purgedToast(result))
|
||||
purgeChangedList = true
|
||||
}
|
||||
|
||||
function onPurgePartial() {
|
||||
purgeChangedList = true
|
||||
}
|
||||
|
||||
function closePurge() {
|
||||
purgeOpen.value = false
|
||||
if (purgeChangedList) {
|
||||
purgeChangedList = false
|
||||
refreshInBackground()
|
||||
}
|
||||
restoreFocusToOpener(null)
|
||||
}
|
||||
|
||||
function mergeText(merged: number, articlesUpdated: number) {
|
||||
const copiesText = merged === 1 ? t('1 copy') : t('{0} copies', [merged])
|
||||
const articlesText =
|
||||
@@ -873,10 +914,13 @@ async function mergeAction() {
|
||||
<MediaToolbar
|
||||
:controls="controls"
|
||||
:counts="counts"
|
||||
:show-purge="showPurge"
|
||||
:purge-disabled="offline"
|
||||
@update:q="updateControls({ q: $event })"
|
||||
@update:filter="updateControls({ filter: $event })"
|
||||
@update:sort="updateControls({ sort: $event })"
|
||||
@update:view="updateControls({ view: $event })"
|
||||
@purge="openPurge"
|
||||
/>
|
||||
|
||||
<MediaUploadPanel
|
||||
@@ -1022,6 +1066,14 @@ async function mergeAction() {
|
||||
@close="bulkDeleteOpen = false"
|
||||
/>
|
||||
|
||||
<PurgeDuplicatesDialog
|
||||
v-if="purgeOpen"
|
||||
@close="closePurge"
|
||||
@purged="onPurged"
|
||||
@partial="onPurgePartial"
|
||||
@review="purgeOpen = false"
|
||||
/>
|
||||
|
||||
<MergeMediaDialog
|
||||
v-if="mergeTarget"
|
||||
:keep="mergeTarget.keep"
|
||||
|
||||
Reference in new issue
Block a user