BLOG-118 Fix to allow nullable published_time to support unpublished posts #121
@ -8,7 +8,7 @@ export const PostInfoResponseSchema = z.object({
|
||||
description: z.string(),
|
||||
preview_image_url: z.url(),
|
||||
labels: z.array(LabelResponseSchema),
|
||||
published_time: z.iso.datetime({ offset: true })
|
||||
published_time: z.iso.datetime({ offset: true }).nullable()
|
||||
});
|
||||
|
||||
export class PostInfoResponseDto {
|
||||
@ -17,7 +17,7 @@ export class PostInfoResponseDto {
|
||||
readonly description: string;
|
||||
readonly previewImageUrl: URL;
|
||||
readonly labels: readonly LabelResponseDto[];
|
||||
readonly publishedTime: Date;
|
||||
readonly publishedTime: Date | null;
|
||||
|
||||
private constructor(props: {
|
||||
id: number;
|
||||
@ -25,7 +25,7 @@ export class PostInfoResponseDto {
|
||||
description: string;
|
||||
previewImageUrl: URL;
|
||||
labels: LabelResponseDto[];
|
||||
publishedTime: Date;
|
||||
publishedTime: Date | null;
|
||||
}) {
|
||||
this.id = props.id;
|
||||
this.title = props.title;
|
||||
@ -37,13 +37,19 @@ export class PostInfoResponseDto {
|
||||
|
||||
static fromJson(json: unknown): PostInfoResponseDto {
|
||||
const parsedJson = PostInfoResponseSchema.parse(json);
|
||||
|
||||
let published_time: Date | null = null;
|
||||
if (parsedJson.published_time !== null) {
|
||||
published_time = new Date(parsedJson.published_time);
|
||||
}
|
||||
|
||||
return new PostInfoResponseDto({
|
||||
id: parsedJson.id,
|
||||
title: parsedJson.title,
|
||||
description: parsedJson.description,
|
||||
previewImageUrl: new URL(parsedJson.preview_image_url),
|
||||
labels: parsedJson.labels.map((label) => LabelResponseDto.fromJson(label)),
|
||||
publishedTime: new Date(parsedJson.published_time)
|
||||
publishedTime: published_time
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ export class PostInfoViewModel {
|
||||
readonly description: string;
|
||||
readonly previewImageUrl: URL;
|
||||
readonly labels: readonly LabelViewModel[];
|
||||
readonly publishedTime: Date;
|
||||
readonly publishedTime: Date | null;
|
||||
|
||||
private constructor(props: {
|
||||
id: number;
|
||||
@ -18,7 +18,7 @@ export class PostInfoViewModel {
|
||||
description: string;
|
||||
previewImageUrl: URL;
|
||||
labels: readonly LabelViewModel[];
|
||||
publishedTime: Date;
|
||||
publishedTime: Date | null;
|
||||
}) {
|
||||
this.id = props.id;
|
||||
this.title = props.title;
|
||||
@ -40,18 +40,27 @@ export class PostInfoViewModel {
|
||||
}
|
||||
|
||||
static rehydrate(props: DehydratedPostInfoProps): PostInfoViewModel {
|
||||
let publishedTime: Date | null = null;
|
||||
if (props.publishedTime !== null) {
|
||||
publishedTime = new Date(props.publishedTime);
|
||||
}
|
||||
|
||||
return new PostInfoViewModel({
|
||||
id: props.id,
|
||||
title: props.title,
|
||||
description: props.description,
|
||||
previewImageUrl: new URL(props.previewImageUrl),
|
||||
labels: props.labels.map((label) => LabelViewModel.rehydrate(label)),
|
||||
publishedTime: new Date(props.publishedTime)
|
||||
publishedTime: publishedTime
|
||||
});
|
||||
}
|
||||
|
||||
get formattedPublishedTime(): string {
|
||||
return this.publishedTime.toISOString().slice(0, 10);
|
||||
get isPublished(): boolean {
|
||||
return this.publishedTime !== null;
|
||||
}
|
||||
|
||||
get formattedPublishedTime(): string | null {
|
||||
return this.publishedTime?.toISOString().slice(0, 10) ?? null;
|
||||
}
|
||||
|
||||
dehydrate(): DehydratedPostInfoProps {
|
||||
@ -61,7 +70,7 @@ export class PostInfoViewModel {
|
||||
description: this.description,
|
||||
previewImageUrl: this.previewImageUrl.href,
|
||||
labels: this.labels.map((label) => label.dehydrate()),
|
||||
publishedTime: this.publishedTime.getTime()
|
||||
publishedTime: this.publishedTime?.getTime() ?? null
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -72,5 +81,5 @@ export interface DehydratedPostInfoProps {
|
||||
description: string;
|
||||
previewImageUrl: string;
|
||||
labels: DehydratedLabelProps[];
|
||||
publishedTime: number;
|
||||
publishedTime: number | null;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ export class PostInfo {
|
||||
readonly description: string;
|
||||
readonly previewImageUrl: URL;
|
||||
readonly labels: readonly Label[];
|
||||
readonly publishedTime: Date;
|
||||
readonly publishedTime: Date | null;
|
||||
|
||||
constructor(props: {
|
||||
id: number;
|
||||
@ -14,7 +14,7 @@ export class PostInfo {
|
||||
description: string;
|
||||
previewImageUrl: URL;
|
||||
labels: readonly Label[];
|
||||
publishedTime: Date;
|
||||
publishedTime: Date | null;
|
||||
}) {
|
||||
this.id = props.id;
|
||||
this.title = props.title;
|
||||
|
@ -22,15 +22,17 @@
|
||||
<title>{generateTitle(state.data?.info.title)}</title>
|
||||
{#if state.data}
|
||||
<meta name="description" content={state.data.info.description} />
|
||||
{#if state.data.info.isPublished}
|
||||
<StructuredData
|
||||
headline={state.data.info.title}
|
||||
description={state.data.info.description}
|
||||
datePublished={state.data.info.publishedTime}
|
||||
datePublished={state.data.info.publishedTime!}
|
||||
image={state.data.info.previewImageUrl}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
</svelte:head>
|
||||
<article class="container prose pb-10 prose-gray">
|
||||
<article class="prose prose-gray container pb-10">
|
||||
{#if state.data}
|
||||
<PostContentHeader postInfo={state.data.info} />
|
||||
<div class="max-w-3xl">
|
||||
|
Loading…
x
Reference in New Issue
Block a user