Vercel/src/tag/index.ts

177 lines
4.3 KiB
TypeScript

import {
Photo,
PhotoDateRange,
descriptionForPhotoSet,
photoQuantityText,
} from '@/photo';
import {
absolutePathForTag,
absolutePathForTagImage,
getPathComponents,
} from '@/app/paths';
import {
capitalizeWords,
convertStringToArray,
formatCount,
formatCountDescriptive,
} from '@/utility/string';
import { CategoryQueryMeta, sortCategoryByCount } from '@/category';
import { AppTextState } from '@/i18n/state';
// Reserved tags
export const TAG_FAVS = 'favs';
export const TAG_HIDDEN = 'hidden';
type TagWithMeta = { tag: string } & CategoryQueryMeta;
export type Tags = TagWithMeta[]
export const formatTag = (tag?: string) =>
capitalizeWords(tag?.replaceAll('-', ' '));
export const getValidationMessageForTags = (tags?: string) => {
const reservedTags = (convertStringToArray(tags) ?? [])
.filter(tag => isTagFavs(tag) || isTagHidden(tag))
.map(tag => tag.toLocaleUpperCase());
return reservedTags.length
? `Reserved tags: ${reservedTags.join(', ').toLocaleLowerCase()}`
: undefined;
};
export const titleForTag = (
tag: string,
photos:Photo[] = [],
appText: AppTextState,
explicitCount?: number,
) => [
formatTag(tag),
photoQuantityText(explicitCount ?? photos.length, appText),
].join(' ');
export const shareTextForTag = (
tag: string,
appText: AppTextState,
) =>
isTagFavs(tag)
? appText.category.taggedFavs
: appText.category.taggedPhrase(formatTag(tag));
export const sortTagsArray = (
tags: string[],
tagToExclude?: string,
) => tags
.filter(tag => tag !== tagToExclude)
.sort((a, b) => isTagFavs(a) ? -1 : a.localeCompare(b));
export const sortTags = (
tags: Tags,
tagToExclude?: string,
) => tags
.filter(({ tag }) => tag!== tagToExclude)
.sort(({ tag: a }, { tag: b }) =>
isTagFavs(a)
? -1
: isTagFavs(b)
? 1
: a.localeCompare(b));
export const sortTagsByCount = (
tags: Tags,
tagToExclude?: string,
) => tags
.filter(({ tag }) => tag !== tagToExclude)
.sort(({ tag: tagA, count: countA }, { tag: tagB, count: countB }) =>
isTagFavs(tagA)
? -1
: isTagFavs(tagB)
? 1
: countB - countA);
export const sortTagsWithoutFavs = (tags: string[]) =>
sortTagsArray(tags, TAG_FAVS);
export const sortTagsObjectWithoutFavs = (tags: Tags) =>
sortTags(tags, TAG_FAVS);
export const descriptionForTaggedPhotos = (
photos: Photo[] = [],
appText: AppTextState,
dateBased?: boolean,
explicitCount?: number,
explicitDateRange?: PhotoDateRange,
) =>
descriptionForPhotoSet(
photos,
appText,
appText.category.taggedPhotos,
dateBased,
explicitCount,
explicitDateRange,
);
export const generateMetaForTag = (
tag: string,
photos: Photo[],
appText: AppTextState,
explicitCount?: number,
explicitDateRange?: PhotoDateRange,
) => ({
url: absolutePathForTag(tag),
title: titleForTag(tag, photos, appText, explicitCount),
description: descriptionForTaggedPhotos(
photos,
appText,
true,
explicitCount,
explicitDateRange,
),
images: absolutePathForTagImage(tag),
});
export const isTagFavs = (tag: string) => tag.toLocaleLowerCase() === TAG_FAVS;
export const isPhotoFav = ({ tags }: Photo) => tags.some(isTagFavs);
export const isPathFavs = (pathname?: string) =>
getPathComponents(pathname).tag === TAG_FAVS;
export const isTagHidden = (tag: string) => tag.toLowerCase() === TAG_HIDDEN;
export const addHiddenToTags = (
tags: Tags,
countHidden = 0,
lastModifiedHidden = new Date(),
) =>
countHidden > 0
? tags
.filter(({ tag }) => tag === TAG_FAVS)
.concat({
tag: TAG_HIDDEN,
count: countHidden,
lastModified: lastModifiedHidden,
})
.concat(tags
.filter(({ tag }) => tag !== TAG_FAVS)
.sort(sortCategoryByCount),
)
: tags;
export const convertTagsForForm = (
tags: Tags = [],
appText: AppTextState,
) =>
sortTagsObjectWithoutFavs(tags)
.map(({ tag, count }) => ({
value: tag,
annotation: formatCount(count),
annotationAria:
formatCountDescriptive(count, appText.category.taggedPhotos),
}));
export const limitTagsByCount = (tags: Tags, minimumCount: number) =>
tags.filter(({ tag, count }) => (
count >= minimumCount ||
isTagFavs(tag) ||
isTagHidden(tag)
));