132 lines
4.0 KiB
TypeScript
132 lines
4.0 KiB
TypeScript
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<T extends string>(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<string, string> = {}
|
|
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}`
|
|
}
|