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