139 lines
2.9 KiB
TypeScript
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,
|
|
}
|
|
}
|