From aa64d6faec0a06b00e3d04f1408a976570824cbe Mon Sep 17 00:00:00 2001 From: Paulo Pinto Date: Mon, 23 Sep 2024 17:24:04 +0100 Subject: [PATCH 1/8] Must set the Content-Type header on POST requests --- src/api/ApiClient.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/api/ApiClient.ts b/src/api/ApiClient.ts index bacad99f..cc531bf0 100644 --- a/src/api/ApiClient.ts +++ b/src/api/ApiClient.ts @@ -56,8 +56,12 @@ export class ApiClient { const response = await this.playgroundClient.request( { url: `/index.php?rest_route=/wp/v2${ route }`, method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, body: JSON.stringify( body ), } ); + if ( response.httpStatusCode < 200 || response.httpStatusCode >= 300 ) { console.error( response ); throw Error( response.json.message ); From fa0e2ca8b08ac709046495a2b80855595877ddaf Mon Sep 17 00:00:00 2001 From: Paulo Pinto Date: Mon, 23 Sep 2024 17:29:05 +0100 Subject: [PATCH 2/8] Save post content --- src/api/ApiClient.ts | 7 +++++-- src/ui/flows/blog-post/SelectContent.tsx | 10 ++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/api/ApiClient.ts b/src/api/ApiClient.ts index cc531bf0..0d5af39c 100644 --- a/src/api/ApiClient.ts +++ b/src/api/ApiClient.ts @@ -25,9 +25,12 @@ export class ApiClient { } ) ) as Post; } - async updatePost( id: number, data: { title: string } ): Promise< Post > { + async updatePost( + id: number, + data: { title?: string; content?: string } + ): Promise< Post > { return ( await this.post( `/liberated_posts/${ id }`, { - title: data.title, + content: data.content, } ) ) as Post; } diff --git a/src/ui/flows/blog-post/SelectContent.tsx b/src/ui/flows/blog-post/SelectContent.tsx index fa26f34c..1cf6205d 100644 --- a/src/ui/flows/blog-post/SelectContent.tsx +++ b/src/ui/flows/blog-post/SelectContent.tsx @@ -79,18 +79,20 @@ export function SelectContent( props: { post: Post; onExit: () => void } ) { // Save the post when selections happen. useEffect( () => { - if ( ! title ) { + if ( ! content ) { return; } apiClient ?.updatePost( post.id, { - title: title.cleanHtml ?? post.title.raw ?? '', + content: content.cleanHtml ?? post.content.raw ?? '', } ) - .then( () => playgroundClient.goTo( post.link ) ); + .then( () => { + playgroundClient.goTo( post.link ); + } ); }, // The dependencies are correct, we only want to trigger it when the title changes. // eslint-disable-next-line react-hooks/exhaustive-deps - [ title ] + [ content ] ); const isValid = title && date && content; From ba6aac05f876637417cd8114b10d908303e73ed0 Mon Sep 17 00:00:00 2001 From: Paulo Pinto Date: Mon, 23 Sep 2024 17:41:28 +0100 Subject: [PATCH 3/8] Extract logic to function --- src/ui/flows/blog-post/SelectContent.tsx | 27 ++++++++++++------------ 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/ui/flows/blog-post/SelectContent.tsx b/src/ui/flows/blog-post/SelectContent.tsx index 1cf6205d..01cb8579 100644 --- a/src/ui/flows/blog-post/SelectContent.tsx +++ b/src/ui/flows/blog-post/SelectContent.tsx @@ -76,21 +76,22 @@ export function SelectContent( props: { post: Post; onExit: () => void } ) { setLastClickedElement( undefined ); }, [ waitingForSelection, lastClickedElement ] ); + const saveField = async ( field: string, value: any ) => { + if ( ! value || ! apiClient ) { + return; + } + const body: { content?: string } = {}; + if ( field === 'content' ) { + body.content = value; + } + apiClient.updatePost( post.id, body ).then( () => { + playgroundClient.goTo( post.link ); + } ); + }; + // Save the post when selections happen. useEffect( - () => { - if ( ! content ) { - return; - } - apiClient - ?.updatePost( post.id, { - content: content.cleanHtml ?? post.content.raw ?? '', - } ) - .then( () => { - playgroundClient.goTo( post.link ); - } ); - }, - // The dependencies are correct, we only want to trigger it when the title changes. + () => void saveField( 'content', content?.cleanHtml ), // eslint-disable-next-line react-hooks/exhaustive-deps [ content ] ); From f18e8e0b34a46a2eb6ec61b46565ce183a036e71 Mon Sep 17 00:00:00 2001 From: Paulo Pinto Date: Mon, 23 Sep 2024 17:47:00 +0100 Subject: [PATCH 4/8] Simplify function --- src/api/ApiClient.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/api/ApiClient.ts b/src/api/ApiClient.ts index 0d5af39c..2afebd81 100644 --- a/src/api/ApiClient.ts +++ b/src/api/ApiClient.ts @@ -27,11 +27,9 @@ export class ApiClient { async updatePost( id: number, - data: { title?: string; content?: string } + body: { content?: string } ): Promise< Post > { - return ( await this.post( `/liberated_posts/${ id }`, { - content: data.content, - } ) ) as Post; + return ( await this.post( `/liberated_posts/${ id }`, body ) ) as Post; } // eslint-disable-next-line @typescript-eslint/no-unused-vars From 4d7c0ab237e7cdf46d65b73073a005826a434675 Mon Sep 17 00:00:00 2001 From: Paulo Pinto Date: Mon, 23 Sep 2024 17:54:55 +0100 Subject: [PATCH 5/8] Also save the raw html --- src/api/ApiClient.ts | 2 +- src/ui/flows/blog-post/SelectContent.tsx | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/api/ApiClient.ts b/src/api/ApiClient.ts index 2afebd81..096993a7 100644 --- a/src/api/ApiClient.ts +++ b/src/api/ApiClient.ts @@ -27,7 +27,7 @@ export class ApiClient { async updatePost( id: number, - body: { content?: string } + body: { content?: { clean: string; raw: string } } ): Promise< Post > { return ( await this.post( `/liberated_posts/${ id }`, body ) ) as Post; } diff --git a/src/ui/flows/blog-post/SelectContent.tsx b/src/ui/flows/blog-post/SelectContent.tsx index 01cb8579..23253da1 100644 --- a/src/ui/flows/blog-post/SelectContent.tsx +++ b/src/ui/flows/blog-post/SelectContent.tsx @@ -76,13 +76,17 @@ export function SelectContent( props: { post: Post; onExit: () => void } ) { setLastClickedElement( undefined ); }, [ waitingForSelection, lastClickedElement ] ); - const saveField = async ( field: string, value: any ) => { - if ( ! value || ! apiClient ) { + const saveField = async ( + field: string, + clean?: string, + original?: string + ) => { + if ( ! clean || ! original || ! apiClient ) { return; } - const body: { content?: string } = {}; + const body: { content?: { clean: string; raw: string } } = {}; if ( field === 'content' ) { - body.content = value; + body.content = { clean, raw: original }; } apiClient.updatePost( post.id, body ).then( () => { playgroundClient.goTo( post.link ); @@ -91,7 +95,12 @@ export function SelectContent( props: { post: Post; onExit: () => void } ) { // Save the post when selections happen. useEffect( - () => void saveField( 'content', content?.cleanHtml ), + () => + void saveField( + 'content', + content?.cleanHtml, + content?.originalHtml + ), // eslint-disable-next-line react-hooks/exhaustive-deps [ content ] ); From 6cc58fab2b1d37ccef530faedaa9d62a2a468556 Mon Sep 17 00:00:00 2001 From: Paulo Pinto Date: Mon, 23 Sep 2024 18:00:53 +0100 Subject: [PATCH 6/8] Save the title --- src/ui/flows/blog-post/SelectContent.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ui/flows/blog-post/SelectContent.tsx b/src/ui/flows/blog-post/SelectContent.tsx index 23253da1..9aeb4258 100644 --- a/src/ui/flows/blog-post/SelectContent.tsx +++ b/src/ui/flows/blog-post/SelectContent.tsx @@ -84,16 +84,27 @@ export function SelectContent( props: { post: Post; onExit: () => void } ) { if ( ! clean || ! original || ! apiClient ) { return; } - const body: { content?: { clean: string; raw: string } } = {}; + const body: { + title?: { clean: string; raw: string }; + content?: { clean: string; raw: string }; + } = {}; if ( field === 'content' ) { body.content = { clean, raw: original }; } + if ( field === 'title' ) { + body.title = { clean, raw: original }; + } apiClient.updatePost( post.id, body ).then( () => { playgroundClient.goTo( post.link ); } ); }; // Save the post when selections happen. + useEffect( + () => void saveField( 'title', title?.cleanHtml, title?.originalHtml ), + // eslint-disable-next-line react-hooks/exhaustive-deps + [ title ] + ); useEffect( () => void saveField( From 4d7a01abe82029b63a1d8a7704e022ecab275eb7 Mon Sep 17 00:00:00 2001 From: Paulo Pinto Date: Mon, 23 Sep 2024 18:03:25 +0100 Subject: [PATCH 7/8] Add UpdatePostBody and CreatePostBody types --- src/api/ApiClient.ts | 19 ++++++++++++------- src/ui/flows/blog-post/SelectContent.tsx | 6 ++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/api/ApiClient.ts b/src/api/ApiClient.ts index 096993a7..f51df667 100644 --- a/src/api/ApiClient.ts +++ b/src/api/ApiClient.ts @@ -3,6 +3,15 @@ import { PlaygroundClient } from '@wp-playground/client'; import { Post } from '@/api/Post'; +export interface CreatePostBody { + guid: string; +} + +export interface UpdatePostBody { + title?: { clean: string; raw: string }; + content?: { clean: string; raw: string }; +} + export class ApiClient { private readonly playgroundClient: PlaygroundClient; private readonly _siteUrl: string; @@ -16,19 +25,15 @@ export class ApiClient { return this._siteUrl; } - async createPost( data: { guid: string } ): Promise< Post > { - const { guid } = data; + async createPost( body: CreatePostBody ): Promise< Post > { return ( await this.post( '/liberated_posts', { meta: { - guid, + guid: body.guid, }, } ) ) as Post; } - async updatePost( - id: number, - body: { content?: { clean: string; raw: string } } - ): Promise< Post > { + async updatePost( id: number, body: UpdatePostBody ): Promise< Post > { return ( await this.post( `/liberated_posts/${ id }`, body ) ) as Post; } diff --git a/src/ui/flows/blog-post/SelectContent.tsx b/src/ui/flows/blog-post/SelectContent.tsx index 9aeb4258..96682108 100644 --- a/src/ui/flows/blog-post/SelectContent.tsx +++ b/src/ui/flows/blog-post/SelectContent.tsx @@ -5,6 +5,7 @@ import { ContentBus } from '@/bus/ContentBus'; import { cleanHtml } from '@/parser/cleanHtml'; import { Post } from '@/api/Post'; import { useSessionContext } from '@/ui/session/SessionProvider'; +import { UpdatePostBody } from '@/api/ApiClient'; enum section { title = 1, @@ -84,10 +85,7 @@ export function SelectContent( props: { post: Post; onExit: () => void } ) { if ( ! clean || ! original || ! apiClient ) { return; } - const body: { - title?: { clean: string; raw: string }; - content?: { clean: string; raw: string }; - } = {}; + const body: UpdatePostBody = {}; if ( field === 'content' ) { body.content = { clean, raw: original }; } From fca40d8f498440a648596bf4cbbadd0a3c91c8ee Mon Sep 17 00:00:00 2001 From: Paulo Pinto Date: Mon, 23 Sep 2024 18:36:04 +0100 Subject: [PATCH 8/8] Simplify API --- src/api/ApiClient.ts | 23 +++++++++---- src/ui/flows/blog-post/SelectContent.tsx | 42 ++++++++---------------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/api/ApiClient.ts b/src/api/ApiClient.ts index f51df667..bfc7e483 100644 --- a/src/api/ApiClient.ts +++ b/src/api/ApiClient.ts @@ -8,8 +8,8 @@ export interface CreatePostBody { } export interface UpdatePostBody { - title?: { clean: string; raw: string }; - content?: { clean: string; raw: string }; + title?: string; + content?: { cleanHtml: string; originalHtml: string }; } export class ApiClient { @@ -34,7 +34,20 @@ export class ApiClient { } async updatePost( id: number, body: UpdatePostBody ): Promise< Post > { - return ( await this.post( `/liberated_posts/${ id }`, body ) ) as Post; + const actualBody: any = {}; + if ( body.title ) { + actualBody.title = body.title; + } + if ( body.content ) { + actualBody.content = body.content.cleanHtml; + actualBody.meta = { + raw_content: body.content.originalHtml, + }; + } + return ( await this.post( + `/liberated_posts/${ id }`, + actualBody + ) ) as Post; } // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -42,10 +55,6 @@ export class ApiClient { return null; } - async getPosts(): Promise< Post[] > { - return ( await this.get( '/posts' ) ) as Post[]; - } - private async get( route: string ): Promise< object > { const response = await this.playgroundClient.request( { url: `/index.php?rest_route=/wp/v2${ route }`, diff --git a/src/ui/flows/blog-post/SelectContent.tsx b/src/ui/flows/blog-post/SelectContent.tsx index 96682108..13dbd3e3 100644 --- a/src/ui/flows/blog-post/SelectContent.tsx +++ b/src/ui/flows/blog-post/SelectContent.tsx @@ -5,7 +5,6 @@ import { ContentBus } from '@/bus/ContentBus'; import { cleanHtml } from '@/parser/cleanHtml'; import { Post } from '@/api/Post'; import { useSessionContext } from '@/ui/session/SessionProvider'; -import { UpdatePostBody } from '@/api/ApiClient'; enum section { title = 1, @@ -77,39 +76,26 @@ export function SelectContent( props: { post: Post; onExit: () => void } ) { setLastClickedElement( undefined ); }, [ waitingForSelection, lastClickedElement ] ); - const saveField = async ( - field: string, - clean?: string, - original?: string - ) => { - if ( ! clean || ! original || ! apiClient ) { - return; - } - const body: UpdatePostBody = {}; - if ( field === 'content' ) { - body.content = { clean, raw: original }; - } - if ( field === 'title' ) { - body.title = { clean, raw: original }; - } - apiClient.updatePost( post.id, body ).then( () => { - playgroundClient.goTo( post.link ); - } ); - }; - // Save the post when selections happen. useEffect( - () => void saveField( 'title', title?.cleanHtml, title?.originalHtml ), + () => { + if ( apiClient && title ) { + apiClient + .updatePost( post.id, { title: title.cleanHtml } ) + .then( () => playgroundClient.goTo( post.link ) ); + } + }, // eslint-disable-next-line react-hooks/exhaustive-deps [ title ] ); useEffect( - () => - void saveField( - 'content', - content?.cleanHtml, - content?.originalHtml - ), + () => { + if ( apiClient && content ) { + apiClient + .updatePost( post.id, { content } ) + .then( () => playgroundClient.goTo( post.link ) ); + } + }, // eslint-disable-next-line react-hooks/exhaustive-deps [ content ] );