Files
RenewCMS/web/src/composables/useMediaCleanupSettings.ts
T

139 lines
2.9 KiB
TypeScript

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,
}
}