export const THUMBNAIL_DEFAULT = "https://teasanctuary.ru/common/background-day.webp"; export const BLOG_POST_FRESHNESS_MILLIS = 3 * 24 * 60 * 60 * 1000; // 3 дня export type PostComparer = (a: App.BlogPost, b: App.BlogPost) => number; export const sortPostsByPostDate: PostComparer = (a, b) => new Date(b.date!).valueOf() - new Date(a.date!).valueOf(); function laterDate(a: string, b?: string): number { const dateA = new Date(a).valueOf(); if (!b) return dateA; const dateB = new Date(b).valueOf(); return Math.max(dateA, dateB); } export const sortPostsByPostAndUpdateDate: PostComparer = (a, b) => laterDate(b.date!, b.dateChanged) - laterDate(a.date!, a.dateChanged); export async function fetchPostsSorted(postComparer?: PostComparer) { const allPosts = await fetchPosts(); const sortedPosts = allPosts // Для списка постов оставляем только те, у которых объявлена дата публикации .filter((a) => !!a.date) .sort(postComparer ?? sortPostsByPostDate); return sortedPosts; }; // rndtrash: пришлось заменить `path.parse`, так как на стороне клиента его больше не реализуют function getFilenameFromPath(path: string) { const file = path.split("/").pop(); const dot = file?.lastIndexOf('.') ?? -1; return dot === -1 ? file : file?.substring(0, file.lastIndexOf('.')); } export async function fetchPosts() { const allPostFiles = import.meta.glob('/src/blogs/*.md'); const iterablePostFiles = Object.entries(allPostFiles); const allPosts: App.BlogPost[] = await Promise.all( iterablePostFiles.map(async ([filePath, resolver]) => { const { metadata }: any = await resolver(); const name = getFilenameFromPath(filePath); return { slug: name, ...metadata }; }) ); return allPosts; }; export function resolveBlogPath(slug?: string, src?: string) { if (!src) return null; return src.startsWith('http://') || src.startsWith('https://') ? src : `/blog/${slug}/${src}`; } const bptToString: Record = { 'article': 'Заметка', 'event': 'Событие', 'update': 'Обновление' }; const bptToIcon: Record = { 'article': 'material-symbols:article', 'event': 'material-symbols:event', 'update': 'material-symbols:autorenew' }; export function blogPostTypeToString(type: App.BlogPostType): string { return bptToString[type] ?? bptToString['article']; } export function blogPostTypeToIcon(type: App.BlogPostType): string { return bptToIcon[type] ?? bptToIcon['article']; }