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
@@ -0,0 +1,129 @@
|
||||
import { onBeforeUnmount, ref } from 'vue'
|
||||
import {
|
||||
useMediaPurge,
|
||||
type PurgeSettings,
|
||||
type PurgeSettingsPatch,
|
||||
} from '@/composables/useMediaPurge'
|
||||
import { isForbidden, isHandledGlobally } from '@/lib/mediaPurge'
|
||||
|
||||
export type CleanupLoadState = 'loading' | 'ready' | 'error'
|
||||
export type SaveStatus = 'idle' | 'saving' | 'saved'
|
||||
export type SaveFailure = 'enabled' | 'days'
|
||||
|
||||
const SKELETON_DELAY_MS = 150
|
||||
const SAVED_VISIBLE_MS = 2000
|
||||
|
||||
export function useMediaCleanupSettings(onForbidden: () => void) {
|
||||
const { getSettings, updateSettings } = useMediaPurge()
|
||||
|
||||
const settings = ref<PurgeSettings | null>(null)
|
||||
const loadState = ref<CleanupLoadState>('loading')
|
||||
const showSkeleton = ref(false)
|
||||
const saveStatus = ref<SaveStatus>('idle')
|
||||
const saveFailure = ref<SaveFailure | null>(null)
|
||||
let skeletonTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let savedTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let latestLoad = 0
|
||||
let pendingSaves = 0
|
||||
|
||||
function clearSkeletonTimer() {
|
||||
if (skeletonTimer !== null) {
|
||||
clearTimeout(skeletonTimer)
|
||||
skeletonTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
function clearSavedTimer() {
|
||||
if (savedTimer !== null) {
|
||||
clearTimeout(savedTimer)
|
||||
savedTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
async function load() {
|
||||
latestLoad += 1
|
||||
const ticket = latestLoad
|
||||
loadState.value = 'loading'
|
||||
showSkeleton.value = false
|
||||
clearSkeletonTimer()
|
||||
skeletonTimer = setTimeout(() => {
|
||||
showSkeleton.value = true
|
||||
}, SKELETON_DELAY_MS)
|
||||
try {
|
||||
const loaded = await getSettings()
|
||||
if (ticket === latestLoad) {
|
||||
settings.value = loaded
|
||||
loadState.value = 'ready'
|
||||
}
|
||||
} catch (error) {
|
||||
if (ticket !== latestLoad) {
|
||||
return
|
||||
}
|
||||
if (isHandledGlobally(error)) {
|
||||
if (isForbidden(error)) {
|
||||
onForbidden()
|
||||
}
|
||||
return
|
||||
}
|
||||
loadState.value = 'error'
|
||||
} finally {
|
||||
if (ticket === latestLoad) {
|
||||
clearSkeletonTimer()
|
||||
showSkeleton.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
settings.value = await getSettings()
|
||||
} catch (error) {
|
||||
if (isForbidden(error)) {
|
||||
onForbidden()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function finishSaving(succeeded: boolean) {
|
||||
if (pendingSaves > 0) {
|
||||
return
|
||||
}
|
||||
if (!succeeded) {
|
||||
saveStatus.value = 'idle'
|
||||
return
|
||||
}
|
||||
saveStatus.value = 'saved'
|
||||
savedTimer = setTimeout(() => {
|
||||
saveStatus.value = 'idle'
|
||||
}, SAVED_VISIBLE_MS)
|
||||
}
|
||||
|
||||
async function save(patch: PurgeSettingsPatch, field: SaveFailure) {
|
||||
pendingSaves += 1
|
||||
clearSavedTimer()
|
||||
saveStatus.value = 'saving'
|
||||
let succeeded = false
|
||||
try {
|
||||
settings.value = await updateSettings(patch)
|
||||
saveFailure.value = null
|
||||
succeeded = true
|
||||
} catch (error) {
|
||||
if (isForbidden(error)) {
|
||||
onForbidden()
|
||||
} else if (!isHandledGlobally(error)) {
|
||||
saveFailure.value = field
|
||||
}
|
||||
} finally {
|
||||
pendingSaves -= 1
|
||||
finishSaving(succeeded)
|
||||
}
|
||||
return succeeded
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearSkeletonTimer()
|
||||
clearSavedTimer()
|
||||
})
|
||||
|
||||
return { settings, loadState, showSkeleton, saveStatus, saveFailure, load, refresh, save }
|
||||
}
|
||||
Reference in new issue
Block a user