Поддержка событий в карточках блога
This commit is contained in:
parent
17db6d4f3e
commit
9b03d0c197
3 changed files with 101 additions and 30 deletions
|
|
@ -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}
|
||||
<div class="toast {fullClass('hidden', 'sm:hidden')}">
|
||||
{#if eventStatus === EventStatus.NotStarted}
|
||||
СКОРО НАЧАЛО
|
||||
{:else}
|
||||
СОБЫТИЕ
|
||||
{/if}
|
||||
</div>
|
||||
{:else if isPostFresh}
|
||||
<div class="toast {fullClass('hidden', 'sm:hidden')}">
|
||||
{#if isPostUpdated}
|
||||
ОБНОВЛЕНО
|
||||
|
|
@ -99,6 +122,20 @@
|
|||
</div>
|
||||
<div class="flex w-full flex-col justify-center p-4 break-words">
|
||||
<div class="flex flex-row flex-wrap justify-start gap-4 pb-2">
|
||||
{#if isPostEventOfInterest}
|
||||
<DateWidget
|
||||
class={eventHasStarted ? shortClass('hidden', 'not-sm:hidden') : ''}
|
||||
dateString={post.dateEventFrom}
|
||||
type="eventStart"
|
||||
highlight={!eventHasStarted}
|
||||
/>
|
||||
<DateWidget
|
||||
class={!eventHasStarted ? shortClass('hidden', 'not-sm:hidden') : ''}
|
||||
dateString={post.dateEventTo}
|
||||
type="eventFinish"
|
||||
highlight={eventHasStarted}
|
||||
/>
|
||||
{:else}
|
||||
<DateWidget
|
||||
class={post.dateChanged ? shortClass('hidden', 'not-sm:hidden') : ''}
|
||||
dateString={post.date}
|
||||
|
|
@ -118,6 +155,7 @@
|
|||
{blogPostTypeToString(type)}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<h2 class="text-3xl font-bold">{post.title}</h2>
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 @@
|
|||
|
||||
<div
|
||||
class="flex flex-nowrap items-center gap-2 p-1 text-lg font-bold {className} rounded-lg
|
||||
{highlightClasses(type == 'published' ? 'bg-amber-600' : 'bg-purple-600')}
|
||||
{highlightClasses(
|
||||
type === 'published' ? 'bg-amber-600' : type === 'updated' ? 'bg-purple-600' : ''
|
||||
)}
|
||||
{highlightClasses(
|
||||
type === 'eventStart' ? 'bg-rose-600' : type === 'eventFinish' ? 'bg-teal-600' : ''
|
||||
)}
|
||||
{highlightClasses('text-slate-50')}"
|
||||
>
|
||||
{#if icon}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue