import type { LocationQuery } from 'vue-router' import { MEDIA_PAGE_SIZE_KEY, resolvePageSize } from '@/lib/pageSizeMemory' export type MediaFilter = 'all' | 'unused' | 'duplicates' export type MediaSort = 'newest' | 'oldest' | 'name' | 'largest' | 'total_size' export type MediaVariantsFilter = 'all' | 'with' | 'without' export type MediaView = 'grid' | 'list' export interface MediaControls { q: string filter: MediaFilter sort: MediaSort variants: MediaVariantsFilter view: MediaView page: number size: number } export const MEDIA_PAGE_SIZES = [5, 25, 50, 100] export const DEFAULT_MEDIA_PAGE_SIZE = 25 export const MEDIA_FULL_SET_PAGE_SIZE = 100 const FIRST_PAGE = 1 const POSITIVE_INTEGER = /^[1-9]\d*$/ export const DEFAULT_CONTROLS: MediaControls = { q: '', filter: 'all', sort: 'newest', variants: 'all', view: 'grid', page: FIRST_PAGE, size: DEFAULT_MEDIA_PAGE_SIZE, } const FILTERS: MediaFilter[] = ['all', 'unused', 'duplicates'] const SORTS: MediaSort[] = ['newest', 'oldest', 'name', 'largest', 'total_size'] const VARIANTS_FILTERS: MediaVariantsFilter[] = ['all', 'with', 'without'] const VIEWS: MediaView[] = ['grid', 'list'] function firstValue(value: LocationQuery[string] | undefined) { return (Array.isArray(value) ? value[0] : value) ?? '' } function pick(allowed: T[], value: string, fallback: T) { return (allowed as string[]).includes(value) ? (value as T) : fallback } function parsePage(value: string) { return POSITIVE_INTEGER.test(value) ? Number(value) : FIRST_PAGE } function parseSize(value: LocationQuery[string] | undefined, useStoredSize: boolean) { const first = Array.isArray(value) ? value[0] : value const raw = first === null ? '' : first if (raw === undefined && !useStoredSize) { return DEFAULT_MEDIA_PAGE_SIZE } return resolvePageSize(raw, MEDIA_PAGE_SIZE_KEY, MEDIA_PAGE_SIZES, DEFAULT_MEDIA_PAGE_SIZE) } export function parseMediaControls(query: LocationQuery, useStoredSize = true): MediaControls { return { q: firstValue(query.q).trim(), filter: pick(FILTERS, firstValue(query.filter), DEFAULT_CONTROLS.filter), sort: pick(SORTS, firstValue(query.sort), DEFAULT_CONTROLS.sort), variants: pick(VARIANTS_FILTERS, firstValue(query.variants), DEFAULT_CONTROLS.variants), view: pick(VIEWS, firstValue(query.view), DEFAULT_CONTROLS.view), page: parsePage(firstValue(query.page)), size: parseSize(query.size, useStoredSize), } } export function isFullSet(controls: MediaControls) { return controls.filter === 'duplicates' } export function sameMediaControls(left: MediaControls, right: MediaControls) { const samePaging = (isFullSet(left) && isFullSet(right)) || (left.page === right.page && left.size === right.size) return ( left.q === right.q && left.filter === right.filter && left.sort === right.sort && left.variants === right.variants && left.view === right.view && samePaging ) } export function toRouteQuery(controls: MediaControls) { const query: Record = {} if (!isFullSet(controls) && controls.page !== DEFAULT_CONTROLS.page) { query.page = String(controls.page) } if (!isFullSet(controls) && controls.size !== DEFAULT_CONTROLS.size) { query.size = String(controls.size) } if (controls.q) { query.q = controls.q } if (controls.filter !== DEFAULT_CONTROLS.filter) { query.filter = controls.filter } if (controls.sort !== DEFAULT_CONTROLS.sort) { query.sort = controls.sort } if (controls.variants !== DEFAULT_CONTROLS.variants) { query.variants = controls.variants } if (controls.view !== DEFAULT_CONTROLS.view) { query.view = controls.view } return query } export function toRequestQuery(controls: MediaControls) { const parts = [ ...(controls.q ? [`q=${encodeURIComponent(controls.q)}`] : []), `filter=${controls.filter}`, `sort=${controls.sort}`, ...(controls.variants === 'all' ? [] : [`variants=${controls.variants}`]), `page=${controls.page}`, `size=${controls.size}`, ] return parts.join('&') } export function toHashRequestQuery(hash: string) { return `hash=${hash}&page=${FIRST_PAGE}&size=${MEDIA_FULL_SET_PAGE_SIZE}` }