From 7e9e7613bac34962c126ddaeaec899956e1d8a6c Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Sun, 4 Oct 2026 20:03:01 +0100 Subject: [PATCH] Fix markdown draft preview sync in PostIt blog editor --- .../Controls/MarkdownEditorControl.axaml | 51 +++++++ .../Controls/MarkdownEditorControl.axaml.cs | 63 +++++++++ .../PostIt/ViewModels/Blogs/BlogsViewModel.cs | 52 ++++++- src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml | 133 +++++++----------- 4 files changed, 209 insertions(+), 90 deletions(-) create mode 100644 src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml create mode 100644 src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml.cs diff --git a/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml b/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml new file mode 100644 index 000000000..7c639043d --- /dev/null +++ b/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + diff --git a/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml.cs b/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml.cs new file mode 100644 index 000000000..2538f7168 --- /dev/null +++ b/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml.cs @@ -0,0 +1,63 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using AvaloniaEdit.Document; + +namespace PostIt.Controls; + +public partial class MarkdownEditorControl : UserControl +{ + public static readonly StyledProperty MarkdownTextProperty = + AvaloniaProperty.Register(nameof(MarkdownText)); + + public static readonly StyledProperty DocumentProperty = + AvaloniaProperty.Register(nameof(Document)); + + public static readonly StyledProperty IsPreviewVisibleProperty = + AvaloniaProperty.Register(nameof(IsPreviewVisible), false); + + public string? MarkdownText + { + get => GetValue(MarkdownTextProperty); + set => SetValue(MarkdownTextProperty, value); + } + + public TextDocument? Document + { + get => GetValue(DocumentProperty); + set => SetValue(DocumentProperty, value); + } + + public bool IsPreviewVisible + { + get => GetValue(IsPreviewVisibleProperty); + set => SetValue(IsPreviewVisibleProperty, value); + } + + public MarkdownEditorControl() + { + InitializeComponent(); + PropertyChanged += OnPropertyChanged; + } + + private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (e.Property == MarkdownTextProperty) + { + var text = e.NewValue as string ?? string.Empty; + if (Document is null || !string.Equals(Document.Text, text, StringComparison.Ordinal)) + { + Document = new TextDocument(text); + } + } + else if (e.Property == DocumentProperty) + { + var newDocument = e.NewValue as TextDocument; + var text = newDocument?.Text ?? string.Empty; + if (!string.Equals(MarkdownText, text, StringComparison.Ordinal)) + { + MarkdownText = text; + } + } + } +} diff --git a/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs b/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs index 6fe6eccc9..9de63c24c 100644 --- a/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs @@ -39,18 +39,56 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel /// Editor buffer for the post body. Same pattern as /// . - public string DraftArticle + public string? DraftArticle { - get => DraftArticleDocument.Text; - set { + get => DraftArticleDocument?.Text ?? null; + set + { + var currentText = DraftArticleDocument?.Text; + if (string.Equals(currentText, value, StringComparison.Ordinal)) + { + OnPropertyChanged(nameof(DraftArticle)); + return; + } + OnPropertyChanging(nameof(DraftArticle)); - DraftArticleDocument = new TextDocument(value); - this.OnPropertyChanged(nameof(DraftArticle)); + DraftArticleDocument = value is not null ? new TextDocument(value) : null; + OnPropertyChanged(nameof(DraftArticle)); } } - [ObservableProperty] - public partial TextDocument DraftArticleDocument {get; set;} + private TextDocument? _draftArticleDocument; + + public TextDocument? DraftArticleDocument + { + get => _draftArticleDocument; + set + { + if (ReferenceEquals(_draftArticleDocument, value)) + { + return; + } + + if (_draftArticleDocument is not null) + { + _draftArticleDocument.TextChanged -= DraftArticleDocument_TextChanged; + } + + _draftArticleDocument = value; + + if (_draftArticleDocument is not null) + { + _draftArticleDocument.TextChanged += DraftArticleDocument_TextChanged; + } + + OnPropertyChanged(nameof(DraftArticleDocument)); + } + } + + private void DraftArticleDocument_TextChanged(object? sender, EventArgs e) + { + OnPropertyChanged(nameof(DraftArticle)); + } /// Editor buffer for the post's publication state. /// Reflects the server-side IsPublished flag (the diff --git a/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml b/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml index 90397f09d..f8534a094 100644 --- a/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml +++ b/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml @@ -17,10 +17,7 @@ - - + - + @@ -137,91 +134,61 @@ - + + + + + + + + + + + + + + - - + IsPreviewVisible="{Binding IsMarkdownViewerVisible, Mode=TwoWay}" + MarkdownText="{Binding DraftArticle, Mode=TwoWay}" + Document="{Binding DraftArticleDocument, Mode=TwoWay}" /> - - - - - - - - - - - + VerticalAlignment="Stretch"> + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - + -