Abstracted logic to parse tags from string to array and back for the query

This commit is contained in:
Alejandro Celaya 2022-03-13 11:14:30 +01:00
parent 47d30aaa34
commit e632c5b04f
3 changed files with 23 additions and 19 deletions

View file

@ -34,7 +34,6 @@ const ShortUrlsFilteringBar = (colorGenerator: ColorGenerator): FC<ShortUrlsFilt
{ selectedServer, className, order, handleOrderBy }, { selectedServer, className, order, handleOrderBy },
) => { ) => {
const [{ search, tags, startDate, endDate, tagsMode = 'any' }, toFirstPage ] = useShortUrlsQuery(); const [{ search, tags, startDate, endDate, tagsMode = 'any' }, toFirstPage ] = useShortUrlsQuery();
const selectedTags = tags?.split(',') ?? [];
const setDates = pipe( const setDates = pipe(
({ startDate, endDate }: DateRange) => ({ ({ startDate, endDate }: DateRange) => ({
startDate: formatIsoDate(startDate) ?? undefined, startDate: formatIsoDate(startDate) ?? undefined,
@ -47,9 +46,8 @@ const ShortUrlsFilteringBar = (colorGenerator: ColorGenerator): FC<ShortUrlsFilt
(search) => toFirstPage({ search }), (search) => toFirstPage({ search }),
); );
const removeTag = pipe( const removeTag = pipe(
(tag: string) => selectedTags.filter((selectedTag) => selectedTag !== tag), (tag: string) => tags.filter((selectedTag) => selectedTag !== tag),
(tagsList) => tagsList.length === 0 ? undefined : tagsList.join(','), (updateTags) => toFirstPage({ tags: updateTags }),
(tags) => toFirstPage({ tags }),
); );
const canChangeTagsMode = supportsAllTagsFiltering(selectedServer); const canChangeTagsMode = supportsAllTagsFiltering(selectedServer);
const toggleTagsMode = pipe( const toggleTagsMode = pipe(
@ -80,9 +78,9 @@ const ShortUrlsFilteringBar = (colorGenerator: ColorGenerator): FC<ShortUrlsFilt
</div> </div>
</Row> </Row>
{selectedTags.length > 0 && ( {tags.length > 0 && (
<h4 className="mt-3"> <h4 className="mt-3">
{canChangeTagsMode && selectedTags.length > 1 && ( {canChangeTagsMode && tags.length > 1 && (
<div className="float-end ms-2 mt-1"> <div className="float-end ms-2 mt-1">
<TooltipToggleSwitch <TooltipToggleSwitch
checked={tagsMode === 'all'} checked={tagsMode === 'all'}
@ -94,7 +92,7 @@ const ShortUrlsFilteringBar = (colorGenerator: ColorGenerator): FC<ShortUrlsFilt
</div> </div>
)} )}
<FontAwesomeIcon icon={tagsIcon} className="short-urls-filtering-bar__tags-icon me-1" /> <FontAwesomeIcon icon={tagsIcon} className="short-urls-filtering-bar__tags-icon me-1" />
{selectedTags.map((tag) => {tags.map((tag) =>
<Tag colorGenerator={colorGenerator} key={tag} text={tag} clearable onClose={() => removeTag(tag)} />)} <Tag colorGenerator={colorGenerator} key={tag} text={tag} clearable onClose={() => removeTag(tag)} />)}
</h4> </h4>
)} )}

View file

@ -1,5 +1,5 @@
import { pipe } from 'ramda'; import { pipe } from 'ramda';
import { FC, useEffect, useMemo, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { Card } from 'reactstrap'; import { Card } from 'reactstrap';
import { useLocation, useParams } from 'react-router-dom'; import { useLocation, useParams } from 'react-router-dom';
import { determineOrderDir, OrderDir } from '../utils/helpers/ordering'; import { determineOrderDir, OrderDir } from '../utils/helpers/ordering';
@ -35,7 +35,6 @@ const ShortUrlsList = (
// This separated state handling is needed to be able to fall back to settings value, but only once when loaded // This separated state handling is needed to be able to fall back to settings value, but only once when loaded
orderBy ?? settings.shortUrlsList?.defaultOrdering ?? DEFAULT_SHORT_URLS_ORDERING, orderBy ?? settings.shortUrlsList?.defaultOrdering ?? DEFAULT_SHORT_URLS_ORDERING,
); );
const selectedTags = useMemo(() => tags?.split(',') ?? [], [ tags ]);
const { pagination } = shortUrlsList?.shortUrls ?? {}; const { pagination } = shortUrlsList?.shortUrls ?? {};
const handleOrderBy = (field?: ShortUrlsOrderableFields, dir?: OrderDir) => { const handleOrderBy = (field?: ShortUrlsOrderableFields, dir?: OrderDir) => {
toFirstPage({ orderBy: { field, dir } }); toFirstPage({ orderBy: { field, dir } });
@ -46,21 +45,21 @@ const ShortUrlsList = (
const renderOrderIcon = (field: ShortUrlsOrderableFields) => const renderOrderIcon = (field: ShortUrlsOrderableFields) =>
<TableOrderIcon currentOrder={actualOrderBy} field={field} />; <TableOrderIcon currentOrder={actualOrderBy} field={field} />;
const addTag = pipe( const addTag = pipe(
(newTag: string) => [ ...new Set([ ...selectedTags, newTag ]) ].join(','), (newTag: string) => [ ...new Set([ ...tags, newTag ]) ],
(tags) => toFirstPage({ tags }), (updatedTags) => toFirstPage({ tags: updatedTags }),
); );
useEffect(() => { useEffect(() => {
listShortUrls({ listShortUrls({
page, page,
searchTerm: search, searchTerm: search,
tags: selectedTags, tags,
startDate, startDate,
endDate, endDate,
orderBy: actualOrderBy, orderBy: actualOrderBy,
tagsMode, tagsMode,
}); });
}, [ page, search, selectedTags, startDate, endDate, actualOrderBy, tagsMode ]); }, [ page, search, tags, startDate, endDate, actualOrderBy, tagsMode ]);
return ( return (
<> <>

View file

@ -14,7 +14,6 @@ export interface ShortUrlListRouteParams {
} }
interface ShortUrlsQueryCommon { interface ShortUrlsQueryCommon {
tags?: string;
search?: string; search?: string;
startDate?: string; startDate?: string;
endDate?: string; endDate?: string;
@ -23,10 +22,12 @@ interface ShortUrlsQueryCommon {
interface ShortUrlsQuery extends ShortUrlsQueryCommon { interface ShortUrlsQuery extends ShortUrlsQueryCommon {
orderBy?: string; orderBy?: string;
tags?: string;
} }
interface ShortUrlsFiltering extends ShortUrlsQueryCommon { interface ShortUrlsFiltering extends ShortUrlsQueryCommon {
orderBy?: ShortUrlsOrder; orderBy?: ShortUrlsOrder;
tags: string[];
} }
export const useShortUrlsQuery = (): [ShortUrlsFiltering, ToFirstPage] => { export const useShortUrlsQuery = (): [ShortUrlsFiltering, ToFirstPage] => {
@ -37,16 +38,22 @@ export const useShortUrlsQuery = (): [ShortUrlsFiltering, ToFirstPage] => {
const query = useMemo( const query = useMemo(
pipe( pipe(
() => parseQuery<ShortUrlsQuery>(location.search), () => parseQuery<ShortUrlsQuery>(location.search),
({ orderBy, ...rest }: ShortUrlsQuery): ShortUrlsFiltering => !orderBy ? rest : { ({ orderBy, tags, ...rest }: ShortUrlsQuery): ShortUrlsFiltering => {
...rest, const parsedOrderBy = orderBy ? stringToOrder<ShortUrlsOrderableFields>(orderBy) : undefined;
orderBy: stringToOrder<ShortUrlsOrderableFields>(orderBy), const parsedTags = tags?.split(',') ?? [];
return { ...rest, orderBy: parsedOrderBy, tags: parsedTags };
}, },
), ),
[ location.search ], [ location.search ],
); );
const toFirstPageWithExtra = (extra: Partial<ShortUrlsFiltering>) => { const toFirstPageWithExtra = (extra: Partial<ShortUrlsFiltering>) => {
const { orderBy, ...mergedQuery } = { ...query, ...extra }; const { orderBy, tags, ...mergedQuery } = { ...query, ...extra };
const normalizedQuery: ShortUrlsQuery = { ...mergedQuery, orderBy: orderBy && orderToString(orderBy) }; const normalizedQuery: ShortUrlsQuery = {
...mergedQuery,
orderBy: orderBy && orderToString(orderBy),
tags: tags.length > 0 ? tags.join(',') : undefined,
};
const evolvedQuery = stringifyQuery(normalizedQuery); const evolvedQuery = stringifyQuery(normalizedQuery);
const queryString = isEmpty(evolvedQuery) ? '' : `?${evolvedQuery}`; const queryString = isEmpty(evolvedQuery) ? '' : `?${evolvedQuery}`;