Compare commits
3 Commits
ecf31568c8
...
c1d27dc151
Author | SHA1 | Date | |
---|---|---|---|
c1d27dc151 | |||
dd2438153c | |||
430ab2b50e |
14
OliverBooth/Markdown/Renderers/EditorJs/JsonRenderer.cs
Normal file
14
OliverBooth/Markdown/Renderers/EditorJs/JsonRenderer.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Markdig.Renderers;
|
||||||
|
using Markdig.Syntax;
|
||||||
|
|
||||||
|
namespace OliverBooth.Markdown.Renderers.EditorJs;
|
||||||
|
|
||||||
|
public class JsonRenderer : RendererBase
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override object Render(MarkdownObject markdownObject)
|
||||||
|
{
|
||||||
|
return JsonDocument.Parse("""{"blocks": []}""");
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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<HeadingBlock>
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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<int>() ?? 1,
|
||||||
|
Lines = new StringLineGroup(jsonObject["text"]?.GetValue<string>() ?? string.Empty)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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<LiteralInline>)options.GetConverter(typeof(LiteralInline));
|
||||||
|
writer.WriteStringValue(literal.Content.Text);
|
||||||
|
converter.Write(writer, literal, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
}
|
@ -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<LiteralInline>
|
||||||
|
{
|
||||||
|
public override LiteralInline? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> bytes = reader.ValueSpan;
|
||||||
|
Span<char> 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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<MarkdownDocument>
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override MarkdownDocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var document = new MarkdownDocument();
|
||||||
|
var blocks = JsonSerializer.Deserialize<Block[]>(ref reader, options) ?? Array.Empty<Block>();
|
||||||
|
|
||||||
|
foreach (Block block in blocks)
|
||||||
|
{
|
||||||
|
document.Add(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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<ParagraphBlock>)options.GetConverter(typeof(ParagraphBlock));
|
||||||
|
converter.Write(writer, paragraphBlock, options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HeadingBlock headingBlock:
|
||||||
|
{
|
||||||
|
var converter = (JsonConverter<HeadingBlock>)options.GetConverter(typeof(HeadingBlock));
|
||||||
|
converter.Write(writer, headingBlock, options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<ParagraphBlock>
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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>() ?? string.Empty)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
@using System.Text.Json
|
||||||
@using OliverBooth.Common.Data.Blog
|
@using OliverBooth.Common.Data.Blog
|
||||||
@using OliverBooth.Common.Services
|
@using OliverBooth.Common.Services
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
@ -7,6 +8,17 @@
|
|||||||
@code {
|
@code {
|
||||||
private DotNetObjectReference<MarkdownEditor>? _dotNetHelper;
|
private DotNetObjectReference<MarkdownEditor>? _dotNetHelper;
|
||||||
|
|
||||||
|
[JSInvokable]
|
||||||
|
public string GetEditorObject(Guid id)
|
||||||
|
{
|
||||||
|
if (!BlogPostService.TryGetPost(id, out IBlogPost? post))
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(new { blocks = Array.Empty<object>() });
|
||||||
|
}
|
||||||
|
|
||||||
|
return BlogPostService.GetBlogPostEditorObject(post);
|
||||||
|
}
|
||||||
|
|
||||||
[JSInvokable]
|
[JSInvokable]
|
||||||
public void Save(Guid id, string content)
|
public void Save(Guid id, string content)
|
||||||
{
|
{
|
||||||
|
@ -21,3 +21,43 @@ services:
|
|||||||
- SSL_KEY_PATH=${SSL_KEY_PATH}
|
- SSL_KEY_PATH=${SSL_KEY_PATH}
|
||||||
- MASTODON_TOKEN=${MASTODON_TOKEN}
|
- MASTODON_TOKEN=${MASTODON_TOKEN}
|
||||||
- MASTODON_ACCOUNT=${MASTODON_ACCOUNT}
|
- MASTODON_ACCOUNT=${MASTODON_ACCOUNT}
|
||||||
|
|
||||||
|
oliverbooth_api:
|
||||||
|
container_name: api.oliverbooth.dev
|
||||||
|
pull_policy: build
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: OliverBooth.Api/Dockerfile
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: /var/log/oliverbooth/api
|
||||||
|
target: /app/logs
|
||||||
|
- type: bind
|
||||||
|
source: /etc/oliverbooth/api
|
||||||
|
target: /app/data
|
||||||
|
ports:
|
||||||
|
- "2844:2844"
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- SSL_CERT_PATH=${API_SSL_CERT_PATH}
|
||||||
|
- SSL_KEY_PATH=${API_SSL_KEY_PATH}
|
||||||
|
|
||||||
|
oliverbooth_admin:
|
||||||
|
container_name: admin.oliverbooth.dev
|
||||||
|
pull_policy: build
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: OliverBooth.Admin/Dockerfile
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: /var/log/oliverbooth/admin
|
||||||
|
target: /app/logs
|
||||||
|
- type: bind
|
||||||
|
source: /etc/oliverbooth/admin
|
||||||
|
target: /app/data
|
||||||
|
ports:
|
||||||
|
- "2843:2843"
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- SSL_CERT_PATH=${ADMIN_SSL_CERT_PATH}
|
||||||
|
- SSL_KEY_PATH=${ADMIN_SSL_KEY_PATH}
|
||||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -9,6 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@editorjs/code": "^2.9.0",
|
||||||
"@editorjs/header": "^2.8.1",
|
"@editorjs/header": "^2.8.1",
|
||||||
"@editorjs/paragraph": "^2.11.3",
|
"@editorjs/paragraph": "^2.11.3",
|
||||||
"codex-notifier": "^1.1.2",
|
"codex-notifier": "^1.1.2",
|
||||||
@ -35,6 +36,19 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.4.tgz",
|
||||||
"integrity": "sha512-V8N/TY2TGyas4wLrPIFq7bcow68b3gu8DfDt1+rrHPtXxcexadKauRJL6eQgfG7Z0LCrN4boLRawR4S9gjIh/Q=="
|
"integrity": "sha512-V8N/TY2TGyas4wLrPIFq7bcow68b3gu8DfDt1+rrHPtXxcexadKauRJL6eQgfG7Z0LCrN4boLRawR4S9gjIh/Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@editorjs/code": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@editorjs/code/-/code-2.9.0.tgz",
|
||||||
|
"integrity": "sha512-QNOWxF29j6mYl/gM5HJzeGOt3s4laoZCUbuRqj6RkIvLBWMU+ASwjckEiouA61hcYUOMfpw4vQjzhsfC7xm/vA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codexteam/icons": "^0.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@editorjs/code/node_modules/@codexteam/icons": {
|
||||||
|
"version": "0.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.5.tgz",
|
||||||
|
"integrity": "sha512-s6H2KXhLz2rgbMZSkRm8dsMJvyUNZsEjxobBEg9ztdrb1B2H3pEzY6iTwI4XUPJWJ3c3qRKwV4TrO3J5jUdoQA=="
|
||||||
|
},
|
||||||
"node_modules/@editorjs/editorjs": {
|
"node_modules/@editorjs/editorjs": {
|
||||||
"version": "2.29.0",
|
"version": "2.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.29.0.tgz",
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
"webpack-stream": "^7.0.0"
|
"webpack-stream": "^7.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@editorjs/code": "^2.9.0",
|
||||||
"@editorjs/header": "^2.8.1",
|
"@editorjs/header": "^2.8.1",
|
||||||
"@editorjs/paragraph": "^2.11.3",
|
"@editorjs/paragraph": "^2.11.3",
|
||||||
"codex-notifier": "^1.1.2",
|
"codex-notifier": "^1.1.2",
|
||||||
|
@ -6,20 +6,27 @@ import Interop from "./Interop";
|
|||||||
import SaveButtonMode from "./MarkdownEditor/SaveButtonMode";
|
import SaveButtonMode from "./MarkdownEditor/SaveButtonMode";
|
||||||
import EditorJS from "@editorjs/editorjs";
|
import EditorJS from "@editorjs/editorjs";
|
||||||
import Header from "@editorjs/header";
|
import Header from "@editorjs/header";
|
||||||
|
import CodeTool from "@editorjs/code";
|
||||||
import SimpleImage from "./BlockTools/SimpleImage";
|
import SimpleImage from "./BlockTools/SimpleImage";
|
||||||
|
import Utility from "../app/Utility";
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
getCurrentBlogPost().then(post => {
|
getCurrentBlogPost().then(async post => {
|
||||||
if (!post) {
|
if (!post) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Utility.delay(1000); // hack to wait for setDotNetHelper invocation. TODO fix this shit
|
||||||
|
const blocks = JSON.parse(await Interop.invoke<string>("GetEditorObject", post.id));
|
||||||
|
console.log("JSON object is", blocks);
|
||||||
|
|
||||||
// UI.init();
|
// UI.init();
|
||||||
// UI.addSaveButtonListener(savePost);
|
// UI.addSaveButtonListener(savePost);
|
||||||
|
|
||||||
const editor = new EditorJS({
|
const editor = new EditorJS({
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
tools: {
|
tools: {
|
||||||
|
code: CodeTool,
|
||||||
header: {
|
header: {
|
||||||
class: Header,
|
class: Header,
|
||||||
config: {
|
config: {
|
||||||
@ -29,7 +36,8 @@ import SimpleImage from "./BlockTools/SimpleImage";
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
image: SimpleImage
|
image: SimpleImage
|
||||||
}
|
},
|
||||||
|
data: blocks
|
||||||
});
|
});
|
||||||
|
|
||||||
/*const editor = new MarkdownEditor(UI.markdownInput);
|
/*const editor = new MarkdownEditor(UI.markdownInput);
|
||||||
|
@ -16,6 +16,7 @@ class BlogPost {
|
|||||||
private readonly _formattedPublishDate: string;
|
private readonly _formattedPublishDate: string;
|
||||||
private readonly _formattedUpdateDate: string;
|
private readonly _formattedUpdateDate: string;
|
||||||
private readonly _tags: string[];
|
private readonly _tags: string[];
|
||||||
|
private readonly _blockData: [{ id: string, type: string, data: any }];
|
||||||
|
|
||||||
constructor(json: any) {
|
constructor(json: any) {
|
||||||
this._id = json.id;
|
this._id = json.id;
|
||||||
@ -33,6 +34,11 @@ class BlogPost {
|
|||||||
this._formattedPublishDate = json.formattedPublishDate;
|
this._formattedPublishDate = json.formattedPublishDate;
|
||||||
this._formattedUpdateDate = json.formattedUpdateDate;
|
this._formattedUpdateDate = json.formattedUpdateDate;
|
||||||
this._tags = json.tags;
|
this._tags = json.tags;
|
||||||
|
this._blockData = json.blockData;
|
||||||
|
}
|
||||||
|
|
||||||
|
get blockData(): [{ id: string, type: string, data: any }] {
|
||||||
|
return this._blockData;
|
||||||
}
|
}
|
||||||
|
|
||||||
get id(): string {
|
get id(): string {
|
||||||
|
7
src/ts/app/Utility.ts
Normal file
7
src/ts/app/Utility.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
class Utility {
|
||||||
|
public static delay(timeout: number): Promise<void> {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, timeout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Utility;
|
Loading…
Reference in New Issue
Block a user