diff --git a/src/lib/components/BlogCard.svelte b/src/lib/components/BlogCard.svelte index dd02d40..25bf97d 100644 --- a/src/lib/components/BlogCard.svelte +++ b/src/lib/components/BlogCard.svelte @@ -10,7 +10,9 @@ import { BLOG_POST_FRESHNESS_MILLIS, blogPostTypeToIcon, - blogPostTypeToString + blogPostTypeToString, + EventStatus, + postEventStatus } from '$lib/util/Blogs'; import DateWidget from '$lib/components/DateWidget.svelte'; import Icon from '@iconify/svelte'; @@ -26,15 +28,20 @@ let isPostNew = $state(false); let isPostUpdated = $state(false); let isPostFresh = $derived(isPostNew || isPostUpdated); - // TODO: rndtrash: события и их актуальность + let eventStatus: EventStatus | undefined = $state(undefined); + let isPostEventOfInterest = $derived( + type === 'event' && + (eventStatus === EventStatus.NotStarted || eventStatus === EventStatus.InProgress) + ); + let eventHasStarted = $derived(eventStatus === EventStatus.InProgress); onMount(() => { - // rndtrash: Выполняем проверки на клиенте, чтобы плашка не скомпилировалась и потом не потеряла актуальность const dateNow = new Date().valueOf(); isPostNew = dateNow - new Date(post.date!).valueOf() <= BLOG_POST_FRESHNESS_MILLIS; isPostUpdated = post.dateChanged != null && dateNow - new Date(post.dateChanged).valueOf() <= BLOG_POST_FRESHNESS_MILLIS; + eventStatus = postEventStatus(post); }); /** @@ -71,7 +78,15 @@ href="/blog/{post.slug}" class="blog-card {fullHeight ? 'min-h-full' : ''} - {isPostUpdated ? 'updated' : isPostNew ? 'new' : ''} + {isPostEventOfInterest + ? eventHasStarted + ? 'eventOngoing' + : 'eventPreStart' + : isPostUpdated + ? 'updated' + : isPostNew + ? 'new' + : ''} {shortClass('flex-col justify-baseline')} {fullClass('flex-row justify-stretch', 'sm:flex-row sm:justify-stretch')}" > @@ -87,7 +102,15 @@ alt={post.thumbnailAlt ?? 'Миниатюра поста'} /> {/if} - {#if isPostFresh} + {#if isPostEventOfInterest} +
+ {#if eventStatus === EventStatus.NotStarted} + СКОРО НАЧАЛО + {:else} + СОБЫТИЕ + {/if} +
+ {:else if isPostFresh}
{#if isPostUpdated} ОБНОВЛЕНО @@ -99,25 +122,40 @@
- - {#if post.dateChanged} + {#if isPostEventOfInterest} + + {:else} + + {#if post.dateChanged} + + {/if} +
+ + + {blogPostTypeToString(type)} + +
{/if} -
- - - {blogPostTypeToString(type)} - -

{post.title}

@@ -139,7 +177,7 @@ } &.updated { - box-shadow: 0 0 0 4px var(--color-purple-600); + box-shadow: 0 0 0 calc(var(--spacing) * 1) var(--color-purple-600); .toast { @apply bg-purple-600; @@ -147,13 +185,29 @@ } &.new { - box-shadow: 0 0 0 4px var(--color-amber-600); + box-shadow: 0 0 0 calc(var(--spacing) * 1) var(--color-amber-600); .toast { @apply bg-amber-600; } } + &.eventPreStart { + box-shadow: 0 0 0 calc(var(--spacing) * 1) var(--color-rose-600); + + .toast { + @apply bg-rose-600; + } + } + + &.eventOngoing { + box-shadow: 0 0 0 calc(var(--spacing) * 1) var(--color-teal-600); + + .toast { + @apply bg-teal-600; + } + } + img.thumbnail { @apply absolute h-full w-full object-cover transition-transform; diff --git a/src/lib/components/DateWidget.svelte b/src/lib/components/DateWidget.svelte index fd52c69..90065a3 100644 --- a/src/lib/components/DateWidget.svelte +++ b/src/lib/components/DateWidget.svelte @@ -10,7 +10,7 @@ class: className = '' }: { dateString?: string; - type?: 'published' | 'updated'; + type?: 'published' | 'updated' | 'eventStart' | 'eventFinish'; highlight?: boolean; showTime?: boolean; class?: string; @@ -18,7 +18,9 @@ const typeToIcon = { published: 'material-symbols:calendar-today', - updated: 'material-symbols:update' + updated: 'material-symbols:update', + eventStart: 'material-symbols:rocket-launch', + eventFinish: 'material-symbols:sports-score' }; const icon = type ? typeToIcon[type] : undefined; @@ -28,7 +30,12 @@
{#if icon} diff --git a/src/lib/util/Blogs.ts b/src/lib/util/Blogs.ts index e10d18d..3b193dc 100644 --- a/src/lib/util/Blogs.ts +++ b/src/lib/util/Blogs.ts @@ -8,6 +8,11 @@ export enum EventStatus { IsOver } +export function postIsEventOfInterest(post: App.BlogPost): boolean { + const status = postEventStatus(post); + return status === EventStatus.NotStarted || status === EventStatus.InProgress; +} + export function postEventStatus(post: App.BlogPost): EventStatus { if (post.type !== 'event' || post.dateEventFrom === undefined || post.dateEventTo === undefined) { return EventStatus.NotEvent; @@ -27,12 +32,17 @@ 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 { +function laterDate(a: string, ...dates: (string | undefined)[]): number { const dateA = new Date(a).valueOf(); - if (!b) return dateA; + if (dates.length <= 0) return dateA; - const dateB = new Date(b).valueOf(); - return Math.max(dateA, dateB); + let max = dateA; + for (const d of dates) { + if (!d) continue; + const date = new Date(d).valueOf(); + max = Math.max(max, date); + }; + return max; } export const sortPostsByPostAndUpdateDate: PostComparer = (a, b) => laterDate(b.date!, b.dateChanged) - laterDate(a.date!, a.dateChanged);