From 430ab2b50e5e612d6f4882a1f9e93a5b05d7105a Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Sat, 2 Mar 2024 05:30:15 +0000 Subject: [PATCH] feat: populate editor with block content WIP --- .../Renderers/EditorJs/JsonRenderer.cs | 14 +++++ .../ObjectRenderers/HeadingObjectRenderer.cs | 17 ++++++ .../JsonConverters/HeadingBlockConverter.cs | 53 +++++++++++++++++ .../JsonConverters/LiteralInlineConverter.cs | 22 +++++++ .../MarkdownDocumentConverter.cs | 57 +++++++++++++++++++ .../JsonConverters/ParagraphBlockConverter.cs | 35 ++++++++++++ .../Pages/Components/MarkdownEditor.razor | 12 ++++ src/ts/admin/EditBlogPost.ts | 10 +++- src/ts/app/BlogPost.ts | 8 ++- src/ts/app/Utility.ts | 7 +++ 10 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 OliverBooth/Markdown/Renderers/EditorJs/JsonRenderer.cs create mode 100644 OliverBooth/Markdown/Renderers/EditorJs/ObjectRenderers/HeadingObjectRenderer.cs create mode 100644 OliverBooth/Markdown/Renderers/JsonConverters/HeadingBlockConverter.cs create mode 100644 OliverBooth/Markdown/Renderers/JsonConverters/LiteralInlineConverter.cs create mode 100644 OliverBooth/Markdown/Renderers/JsonConverters/MarkdownDocumentConverter.cs create mode 100644 OliverBooth/Markdown/Renderers/JsonConverters/ParagraphBlockConverter.cs create mode 100644 src/ts/app/Utility.ts diff --git a/OliverBooth/Markdown/Renderers/EditorJs/JsonRenderer.cs b/OliverBooth/Markdown/Renderers/EditorJs/JsonRenderer.cs new file mode 100644 index 0000000..ee0d3c0 --- /dev/null +++ b/OliverBooth/Markdown/Renderers/EditorJs/JsonRenderer.cs @@ -0,0 +1,14 @@ +using System.Text.Json; +using Markdig.Renderers; +using Markdig.Syntax; + +namespace OliverBooth.Markdown.Renderers.EditorJs; + +public class JsonRenderer : RendererBase +{ + /// + public override object Render(MarkdownObject markdownObject) + { + return JsonDocument.Parse("""{"blocks": []}"""); + } +} diff --git a/OliverBooth/Markdown/Renderers/EditorJs/ObjectRenderers/HeadingObjectRenderer.cs b/OliverBooth/Markdown/Renderers/EditorJs/ObjectRenderers/HeadingObjectRenderer.cs new file mode 100644 index 0000000..099015d --- /dev/null +++ b/OliverBooth/Markdown/Renderers/EditorJs/ObjectRenderers/HeadingObjectRenderer.cs @@ -0,0 +1,17 @@ +using Markdig.Renderers; +using Markdig.Syntax; + +namespace OliverBooth.Markdown.Renderers.EditorJs.ObjectRenderers; + +public class HeadingObjectRenderer : IMarkdownObjectRenderer +{ + public bool Accept(RendererBase renderer, Type objectType) + { + return renderer.GetType() == typeof(JsonRenderer) && objectType == typeof(HeadingBlock); + } + + public void Write(RendererBase renderer, MarkdownObject objectToRender) + { + + } +} diff --git a/OliverBooth/Markdown/Renderers/JsonConverters/HeadingBlockConverter.cs b/OliverBooth/Markdown/Renderers/JsonConverters/HeadingBlockConverter.cs new file mode 100644 index 0000000..4d201ad --- /dev/null +++ b/OliverBooth/Markdown/Renderers/JsonConverters/HeadingBlockConverter.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using Markdig.Helpers; +using Markdig.Syntax; +using Markdig.Syntax.Inlines; + +namespace OliverBooth.Markdown.Renderers.JsonConverters; + +public sealed class HeadingBlockConverter : JsonConverter +{ + /// + public override HeadingBlock? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var node = JsonNode.Parse(reader.ValueSpan); + if (node is not JsonObject jsonObject) + { + return null; + } + + return new HeadingBlock(null!) + { + Level = jsonObject["level"]?.GetValue() ?? 1, + Lines = new StringLineGroup(jsonObject["text"]?.GetValue() ?? string.Empty) + }; + } + + /// + public override void Write(Utf8JsonWriter writer, HeadingBlock value, JsonSerializerOptions options) + { + if (value.Inline is not { } containerInline) + { + return; + } + + writer.WriteStartObject(); + writer.WriteString("type", "header"); + writer.WriteNumber("level", value.Level); + writer.WritePropertyName("text"); + + foreach (Inline inline in containerInline) + { + if (inline is LiteralInline literal) + { + var converter = (JsonConverter)options.GetConverter(typeof(LiteralInline)); + writer.WriteStringValue(literal.Content.Text); + converter.Write(writer, literal, options); + } + } + + writer.WriteEndObject(); + } +} diff --git a/OliverBooth/Markdown/Renderers/JsonConverters/LiteralInlineConverter.cs b/OliverBooth/Markdown/Renderers/JsonConverters/LiteralInlineConverter.cs new file mode 100644 index 0000000..2ca9554 --- /dev/null +++ b/OliverBooth/Markdown/Renderers/JsonConverters/LiteralInlineConverter.cs @@ -0,0 +1,22 @@ +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Markdig.Syntax.Inlines; + +namespace OliverBooth.Markdown.Renderers.JsonConverters; + +public class LiteralInlineConverter : JsonConverter +{ + public override LiteralInline? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ReadOnlySpan bytes = reader.ValueSpan; + Span chars = stackalloc char[bytes.Length]; + Encoding.UTF8.GetChars(bytes, chars); + return new LiteralInline(chars.ToString()); + } + + public override void Write(Utf8JsonWriter writer, LiteralInline value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.Content.Text); + } +} diff --git a/OliverBooth/Markdown/Renderers/JsonConverters/MarkdownDocumentConverter.cs b/OliverBooth/Markdown/Renderers/JsonConverters/MarkdownDocumentConverter.cs new file mode 100644 index 0000000..eacc3a1 --- /dev/null +++ b/OliverBooth/Markdown/Renderers/JsonConverters/MarkdownDocumentConverter.cs @@ -0,0 +1,57 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Markdig.Syntax; + +namespace OliverBooth.Markdown.Renderers.JsonConverters; + +public sealed class MarkdownDocumentConverter : JsonConverter +{ + /// + public override MarkdownDocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var document = new MarkdownDocument(); + var blocks = JsonSerializer.Deserialize(ref reader, options) ?? Array.Empty(); + + foreach (Block block in blocks) + { + document.Add(block); + } + + return document; + } + + /// + public override void Write(Utf8JsonWriter writer, MarkdownDocument value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteStartArray("blocks"); + + foreach (Block block in value) + { + WriteBlock(writer, options, block); + } + + writer.WriteEndArray(); + writer.WriteEndObject(); + } + + private static void WriteBlock(Utf8JsonWriter writer, JsonSerializerOptions options, Block block) + { + switch (block) + { + case ParagraphBlock paragraphBlock: + { + var converter = (JsonConverter)options.GetConverter(typeof(ParagraphBlock)); + converter.Write(writer, paragraphBlock, options); + break; + } + + case HeadingBlock headingBlock: + { + var converter = (JsonConverter)options.GetConverter(typeof(HeadingBlock)); + converter.Write(writer, headingBlock, options); + break; + } + } + } +} diff --git a/OliverBooth/Markdown/Renderers/JsonConverters/ParagraphBlockConverter.cs b/OliverBooth/Markdown/Renderers/JsonConverters/ParagraphBlockConverter.cs new file mode 100644 index 0000000..5d7e996 --- /dev/null +++ b/OliverBooth/Markdown/Renderers/JsonConverters/ParagraphBlockConverter.cs @@ -0,0 +1,35 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using Markdig.Helpers; +using Markdig.Syntax; + +namespace OliverBooth.Markdown.Renderers.JsonConverters; + +public sealed class ParagraphBlockConverter : JsonConverter +{ + /// + public override ParagraphBlock? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var node = JsonNode.Parse(reader.ValueSpan); + if (node is not JsonObject jsonObject) + { + return null; + } + + return new ParagraphBlock + { + Lines = new StringLineGroup(jsonObject["text"]?.GetValue() ?? string.Empty) + }; + } + + /// + public override void Write(Utf8JsonWriter writer, ParagraphBlock value, JsonSerializerOptions options) + { + string text = string.Join('\n', value.Inline); + writer.WriteStartObject(); + writer.WriteString("type", "paragraph"); + writer.WriteString("text", text); + writer.WriteEndObject(); + } +} diff --git a/OliverBooth/Pages/Components/MarkdownEditor.razor b/OliverBooth/Pages/Components/MarkdownEditor.razor index 97b85bf..34e8bdd 100644 --- a/OliverBooth/Pages/Components/MarkdownEditor.razor +++ b/OliverBooth/Pages/Components/MarkdownEditor.razor @@ -1,3 +1,4 @@ +@using System.Text.Json @using OliverBooth.Common.Data.Blog @using OliverBooth.Common.Services @implements IDisposable @@ -7,6 +8,17 @@ @code { private DotNetObjectReference? _dotNetHelper; + [JSInvokable] + public string GetEditorObject(Guid id) + { + if (!BlogPostService.TryGetPost(id, out IBlogPost? post)) + { + return JsonSerializer.Serialize(new { blocks = Array.Empty() }); + } + + return BlogPostService.GetBlogPostEditorObject(post); + } + [JSInvokable] public void Save(Guid id, string content) { diff --git a/src/ts/admin/EditBlogPost.ts b/src/ts/admin/EditBlogPost.ts index 72cab69..0a62bbe 100644 --- a/src/ts/admin/EditBlogPost.ts +++ b/src/ts/admin/EditBlogPost.ts @@ -7,13 +7,18 @@ import SaveButtonMode from "./MarkdownEditor/SaveButtonMode"; import EditorJS from "@editorjs/editorjs"; import Header from "@editorjs/header"; import SimpleImage from "./BlockTools/SimpleImage"; +import Utility from "../app/Utility"; (() => { - getCurrentBlogPost().then(post => { + getCurrentBlogPost().then(async post => { if (!post) { return; } + await Utility.delay(1000); // hack to wait for setDotNetHelper invocation. TODO fix this shit + const blocks = JSON.parse(await Interop.invoke("GetEditorObject", post.id)); + console.log("JSON object is", blocks); + // UI.init(); // UI.addSaveButtonListener(savePost); @@ -29,7 +34,8 @@ import SimpleImage from "./BlockTools/SimpleImage"; } }, image: SimpleImage - } + }, + data: blocks }); /*const editor = new MarkdownEditor(UI.markdownInput); diff --git a/src/ts/app/BlogPost.ts b/src/ts/app/BlogPost.ts index 1186964..8a2b083 100644 --- a/src/ts/app/BlogPost.ts +++ b/src/ts/app/BlogPost.ts @@ -16,6 +16,7 @@ class BlogPost { private readonly _formattedPublishDate: string; private readonly _formattedUpdateDate: string; private readonly _tags: string[]; + private readonly _blockData: [{ id: string, type: string, data: any }]; constructor(json: any) { this._id = json.id; @@ -33,6 +34,11 @@ class BlogPost { this._formattedPublishDate = json.formattedPublishDate; this._formattedUpdateDate = json.formattedUpdateDate; this._tags = json.tags; + this._blockData = json.blockData; + } + + get blockData(): [{ id: string, type: string, data: any }] { + return this._blockData; } get id(): string { @@ -70,7 +76,7 @@ class BlogPost { get url(): BlogUrl { return this._url; } - + get tags(): string[] { return this._tags; } diff --git a/src/ts/app/Utility.ts b/src/ts/app/Utility.ts new file mode 100644 index 0000000..393b555 --- /dev/null +++ b/src/ts/app/Utility.ts @@ -0,0 +1,7 @@ +class Utility { + public static delay(timeout: number): Promise { + return new Promise(resolve => setTimeout(resolve, timeout)); + } +} + +export default Utility; \ No newline at end of file