summaryrefslogblamecommitdiffstatshomepage
path: root/frontend/src/lib/Navigation.ts
blob: 4dcb99819f50f74bf36a4a162ffd8118c9e7c350 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                                     
                                                               
                                       
                                      
                                   
 



                                 
 









                                                       
 

                                                 

         
                      

 
                                                                                                 


                                                                                         
                                                        




                                                                                                  

                                                            









                                                                                   
                 



















                                                                                                       
                           






































                                                                                           
                                                                                           
              
                                                        




                                                                                 
                                                                
 



                                                                                          
import { goto as svelteGoto } from '$app/navigation';
import { SortDirection, type ComicFilter } from '$gql/graphql';
import JsonURL from '@jsonurl/jsonurl';
import { toastError } from './Toasts';
import type { Key } from './Utils';

export interface PaginationData {
	page: number;
	items: number;
}

export interface SortData<T extends Key> {
	on: T;
	direction: SortDirection;
	seed: number | undefined;
}

function number<T>(value: string | null, fallback: T) {
	if (!value) return fallback;

	const number = +value;

	if (Number.isNaN(number) || number < 0) {
		return fallback;
	}

	return number;
}

export function parseSortData<T extends Key>(params: URLSearchParams, fallback: T): SortData<T> {
	return {
		on: (params.get('s') as T) || fallback,
		direction: (params.get('d') as SortDirection) || SortDirection.Ascending,
		seed: number(params.get('r'), undefined)
	};
}

export function parsePaginationData(params: URLSearchParams, defaultItems = 120): PaginationData {
	return {
		page: number(params.get('p'), 1),
		items: number(params.get('i'), defaultItems)
	};
}

export function parseFilter<T>(params: URLSearchParams): T {
	const param = params.get('f');

	if (!param) return {} as T;

	try {
		return JsonURL.parse(param, { AQF: true, impliedObject: {} }) as T;
	} catch {
		return {} as T;
	}
}

interface NavigationOptions {
	to?: string;
	params: URLSearchParams;
	options?: Parameters<typeof svelteGoto>[1];
}

export function goto({ to = '', params, options }: NavigationOptions) {
	svelteGoto(`${to}?${params.toString()}`, options).catch(() => toastError('Navigation failed'));
}

interface NavigationParameters<T> {
	filter?: T;
	sort?: Partial<SortData<string>>;
	pagination?: Partial<PaginationData>;
}

function parametersFrom<T>(
	{ pagination, filter, sort }: NavigationParameters<T>,
	current?: URLSearchParams
) {
	const params = new URLSearchParams(current);

	if (filter !== undefined) {
		const json = JsonURL.stringify(filter, { AQF: true, impliedObject: true });
		if (json) {
			params.set('f', json);
		} else {
			params.delete('f');
		}
	}

	if (sort !== undefined) {
		if (sort.on !== undefined) {
			params.set('s', sort.on);
		}
		if (sort.direction !== undefined) {
			params.set('d', sort.direction);
		}
		if (sort.seed !== undefined) {
			params.set('r', sort.seed.toString());
		}
	}

	params.delete('p');

	if (pagination?.items) {
		params.set('i', pagination.items.toString());
	}

	if (pagination?.page) {
		params.set('p', pagination.page.toString());
	}

	return params;
}

export function navigate(params: NavigationParameters<object>, current?: URLSearchParams) {
	goto({
		params: parametersFrom(params, current),
		options: { noScroll: false, keepFocus: true, replaceState: true }
	});
}

export function href<T>(base: string, params: NavigationParameters<T>) {
	return `/${base}/?${parametersFrom(params).toString()}`;
}

export function quickComicFilter(id: number | string, filter: keyof ComicFilter) {
	window.open(href('comics', { filter: { include: { [filter]: { all: [id] } } } }));
}